EF code first - map child class property to base class table - asp.net

I wonder if it's possible to map a child class property to base class table. Say I have two classes (shortened):
public abstract class User
{
[Key]
public int UserId { get; set; }
public string Username { get; set; }
// other properties...
}
and
public class Customer : User
{
public int ShopId { get; set; }
public virtual Shop Shop { get; set; }
// other properties...
}
I'm using TPT (table per type) inheritance (that means two tables - User and Customer). For some reasons I would like to have the ShopId property in the User table, but all other properties from Customer class in the Customer table. Is that even possible?
Having ShopId column in User table would allow us for example to create unique index on Username and ShopId (the application is multi-tenant so we don't want globally unique usernames, only shop-level unique usernames).

Is this what you're looking for?
UserBase.cs
public abstract class UserBase
{
public int UserId { get; set; }
public string Username { get; set; }
public int ShopId { get; set; }
public virtual Shop Shop { get; set; }
}
User.cs
public class User : UserBase
{
// user specific properties...
}
Customer.cs
public class Customer : UserBase
{
// customer specific properties...
}
UserDbContext.cs
public class UserDbContext : DbContext
{
...
protected override OnModelCreating(DbModelBuilder modelBuilder)
{
// if you want users and customers to be shop specific
modelBuilder.Entity<UserBase>.HasKey(x => new { x.UserId, x.ShopId });
// if you only want users to be shop specific uncomment below and remove above
//modelBuilder.Entity<User>.HasKey(x => new { x.UserId, x.ShopId });
}
}

Related

EF Core one-to-many relationship with multiple contexts (databases)

I have contexts with entities like this:
public class CompanyContext : DbContext
{
public DbSet<StoreModel> Stores { get; set; }
// Other entities
}
public class DepartmentContext : DbContext
{
public DbSet<OrderModel> Orders { get; set; }
// Other entities
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> ReceivedOrders { get; set; }
public virtual ICollection<OrderModel> PreparedOrders { get; set; }
public virtual ICollection<OrderModel> IssuedOrders { get; set; }
}
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public StoreModel GettingStore { get; set; }
public StoreModel PreparingStore { get; set; }
public StoreModel IssuanceStore { get; set; }
}
For example a user makes an order in storeA, but wants to receive it in storeC, and it order will preparing in storeB. And I needs a statiscics about store received/prepared/issued orders.
When I try to create a migrations, EF throws exceptions "Unable to determine the relationship represented by navigation 'OrderModel.GettingStore' of type 'StoreModel'" and "Unable to determine the relationship represented by navigation 'StoreModel.IssuedOrders' of type 'ICollection<OrderModel>'". If I understand correctly, this happens because entities are defined in different contexts.
Now I just use next model:
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid GettingStoreId { get; set; }
public Guid PreparingStoreId { get; set; }
public Guid IssuanceStoreId { get; set; }
}
This works fine, but perhaps there are options that allow to create such a structure using navigation properties, with correct relationships between these entities from different contexts(databases).
First, the map of a different database was not placed in tables of different application formats, so think that you have a domain that should be well defined in your application, that way you would have the mapping of your application like this:
public class DomainNameContext: DbContext
{
public DomainNameContext(): base()
{
}
public DbSet<StoreModel> Stores { get; set; }
public DbSet<OrderModel> Orders { get; set; }
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
}
}
another thing, the relation you are using doesn't work so you can't have a repetition of Orders within the same class because this is not one -> many, this statement means that a StoreModel line can have many lines in the OrderModel this way would be like this
public class OrderModel
{
public Guid Id { get; set; }
public string Details { get; set; }
public Guid StoreModeId { get; set; } // this part will show the entity framework that this is the fk it will correlate
public StoreModel StoreModel { get; set; }
}
public class StoreModel
{
public Guid Id { get; set; }
public string Name { get; set; }
public virtual ICollection<OrderModel> OrderModels { get; set; }
}
see that if you are wanting to have many StoreModel related to many OrderModel then you need to use many -> many which microsoft documentation foresees to use as well
good to map this within its context it is necessary in OnModelCreating to use its mapping like this:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
// config mapping methods
modelBuilder.Entity<StoreModel>()
.HasMany<OrderModel>(g => g.OrderModels )
.HasForeignkey<Guid>(s => s.StoreModeId )
}
you can have a look at the microsoft documentation enter link description here, enter link description here
now if you need to map between contexts you will have to use dapper to make separate queries in separate bases the entity has support for that in this link enter link description here
and then you can make the necessary inner joins so that you can use it but natively this does not exist, I advise you to rethink your database so that it can make more sense to a relational model, perhaps putting types for your StoreModel and OrderModel so you can use the way I wanted the types GettingStore, PreparingStore, IssuanceStore using an enum for this to make it explicit

Entity Framework shows inconsistent behaviour when used with Asp.net Identity

I have 3 tables Violation,Comment and and auto generated AspNetUsers respectively.The relationship between them as follows.
I am using code-first approach and my models are as follows.Some properties are removed for brevity.
Violation Model
public class Violation
{
public Violation()
{
this.Comments = new HashSet<Comment>();
}
public int Id { get; set; }
public virtual ICollection<Comment> Comments { get; set; }
public virtual ApplicationUser CreatorUser { get; set; }
}
Comment Model
public class Comment
{
public int Id { get; set; }
[Required]
public string Content { get; set; }
[Required]
public DateTime PostedDateTime { get; set; }
public ApplicationUser ApplicationUser { get; set; }
public Violation Violation { get; set; }
}
ApplicationUser(AspNetUsers Table)
public class ApplicationUser : IdentityUser
{
public ApplicationUser()
{
this.Comments = new List<Comment>();
this.Violations = new List<Violation>();
}
public virtual List<Comment> Comments { get; set; }
public virtual List<Violation> Violations { get; set; }
}
The problem is that when I try to retrieve Comment's ApplicationUser navigation property , I see many of them pointing to a null property even database has proper record for each of them.
Shortly,EF doesn't retrieve database records properly.I stuck with it,can't find the reason.
In fact, it's not being lazy-loaded. You didn't add the virtual keyword to your Comment.ApplicationUser property, so Entity Framework cannot override it to add the lazy-loading logic. As a result, it's always going to be null unless you explicitly load it. Add the virtual keyword, and you'll be fine.
If you want the navigation properties populated you need to include them in the query:
var comments = context.Comments
.Include(c => c.Violation)
.Include(c => c.ApplicationUser)
.Where(x => x.Violation.Id == violationId);
https://msdn.microsoft.com/en-us/data/jj574232.aspx#eager

Different user types in ASP.Net Identity 2.0

So, I'm trying to implement different kind of users on my application, first, let's say there's only one kind of user:
public class ApplicationUser : IdentityUser
{
// Other Properties
public int TeacherID { get; set; }
[ForeignKey("TeacherID ")]
public virtual Teacher Teacher { get; set; }
}
public class Teacher
{
[Key]
public int TeacherID { get; set; }
public int UserID { get; set; }
// Other properties
[ForeignKey("UserID")]
public virtual ApplicationUser User { get; set; }
}
There's a one to one relationship between those 2 entities, but what if there's more than one type of user? I can't have that ForeignKey on the User entity, I think I'm going in the wrong direction.
I though about using roles for this, so there's an Admin, a Teacher, an Student, and different kind of roles for each one, but what happens if I want to store extra properties for each kind of role?
public class IdentityUserRole<TKey>
{
public IdentityUserRole();
// Resumen:
// RoleId for the role
public virtual TKey RoleId { get; set; }
//
// Resumen:
// UserId for the user that is in the role
public virtual TKey UserId { get; set; }
}
I mean, I can extend the class IdentityUserRole and add more properties, but how do I add properties for each kind of role?
It certainly makes sense to use roles for this purpose, but it does mean you could assign multiple roles. so a user could be a Teacher and a Student, but that can happen.
If you want to add extra properties to the role class, it's done in the same way as is done for user. Create your own version of Role like this:
public class ApplicationRole : IdentityRole
{
public string bool CanJuggle { get; set; }
}
And you need a RoleManager class to go with it:
public class ApplicationRoleManager : RoleManager<ApplicationRole>
{
public ApplicationRoleManager(IRoleStore<ApplicationRole> store)
: base(store)
{ }
//snip
}
And not forgetting your context needs to change:
public class YourContext : IdentityDbContext<ApplicationUser, ApplicationRole, string, IdentityUserLogin, IdentityUserRole, IdentityUserClaim>
{
//snip
}
Think that covers all the relevant parts.

EF Code First composite key mapping

I have 2 tables. User and Roles.One user can have meany roles. so i created another table called RoleUser. how should i implement this in code first(i want to insert and update operation)
thanks in advance
Natively you should be able to declare the two tables (with properties pointing to the other) and EF will pick up on the many-to-many relationship (and create the intermediary table with two FK's)
public class user
{
public int id { get; set; }
public string username { get; set; }
// user can be within multiple roles
public ICollection<role> roles { get; set; }
}
public class role
{
public int id { get; set; }
public string name { get; set; }
// role can have many users
public ICollection<user> users { get; set; }
}

Handling default values for models in EF4

I'm wondering what's the best way to handle default values for relationships when making models. (Specifically EF4)
For example, my Organization has a default Contact and I was wondering which one was the best approach. I got these two options (or any other anyone suggests if better)
Using Relationship:
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
}
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Contact> Contacts { get; set; }
//Use a relationship for the default contact?
public Contact DefaultContact { get; set; }
}
Using Value:
public class Contact
{
public int Id { get; set; }
public string FirstName { get; set; }
//Use value?
public boolean IsDefault { get; set; }
}
public class Organization
{
public int Id { get; set; }
public string Name { get; set; }
public ICollection<Contact> Contacts { get; set; }
}
I'd go with Option 1. While 2 is definitely easier to implement, it doesn't enforce rules such as "There cannot be 2 default contacts". I end up with something like the following:
public class Organization {
// ...
public virtual ICollection<Contact> { get;set; }
[ForeignKey("DefaultContactId")]
public Contact DefaultContact { get;set; }
public int? DefaultContactId { get;set; }
}
There's a limitation of this approach - it doesn't work nested deletes (see this question for more details). Because of this, you need to disable CascadeOnDelete for the 1-to-many relationship:
protected override void OnModelCreating(DbModelBuilder modelBuilder)
{
modelBuilder.Entity<Contact>().HasRequired(co => co.Organization).WithMany().WillCascadeOnDelete(false);
}
(Code done without testing, but should work)
The other problem with this is that it's not possible to add the Default Contact at the same time as you're adding the organization, as EF can't figure out the correct order of statements. You need to call .SaveChanges between each. You can still use a TransactionScope to overcome this, but it's not clean:
using (var ts = new TransactionScope())
{
Organization org = new Organization
{
// ...
Contacts = new Collection<Contact>()
}
org.Contacts = new Contact() {};
orgRepo.SaveChanges();
// Now wire up the default contact
org.DefaultContact = org.Contacts.First();
orgRepo.SaveChanges();
}

Resources