I have a problem with the realization of data hierarchy in EF Core 3.0 (I need to use TPH).
Let's look:
public abstract class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public abstract UserProfile **Profile** { get; set; }
}
public class ConcreteUser1 : User
{
public string ConcreteUser1Prop { get; set; }
public override UserProfile Profile { get; set; }
}
public class ConcreteUser2 : User
{
public string ConcreteUser2Prop { get; set; }
public override UserProfile Profile { get; set; }
}
And here we have Profile
classes:
public class UserProfile
{
public Guid Id { get; set; }
public string SameProp { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
}
public class ConcreteUser1Profile : Profile
{
public string ConcreteProfile1Prop { get; set; }
}
public class ConcreteUser2Profile : Profile
{
public string ConcreteProfile2Prop { get; set; }
}
Let it be only one DbSet<User>
which can be used for getting data from Database.
So, I can't understand, how to say EF Core (3.0) 2 things: how to say to store ConcreteUser*Profile
(possible, Add
and SaveChanges
will work properly and write concrete profile type when I add concrete type user with concrete profile type. But I don't know how to say EF Core to get the correct concrete profile type when I use Where
or FirstOrDefault
method?
Is this model correct in principle?
Upd.
For example, I have 2 records in my Db: ConcreteUser1 user1
, ConcreteUser2 user2
. So, let's have DbSet<User> Users
, so, what will I have after the request: var tstUser = ExampleDbContext.Users.FirstOrDefault();
? What will the type of Profile variable in tstUser.Profile
?
I have a solution (maybe someone will use it too).
public class UserProfile
{
public Guid Id { get; set; }
public ProfileRoles Role { get; set;}
public string SameProp { get; set; }
public Guid UserId { get; set; }
public User User { get; set; }
}
public class ConcreteUser1Profile : UserProfile
{
public string ConcreteProfile1Prop { get; set; }
}
public class ConcreteUser2Profile : UserProfile
{
public string ConcreteProfile2Prop { get; set; }
}
and
public class User
{
public Guid Id { get; set; }
public string Name { get; set; }
public UserRoles Role { get; set; }
public UserProfile Profile { get; set; }
}
public class ConcreteUser1 : User
{
public string ConcreteUser1Prop { get; set; }
}
public class ConcreteUser2 : User
{
public string ConcreteUser2Prop { get; set; }
}
public enum UserRoles
{
User = 0,
ConcreteUser1 = 1,
ConcreteUser2 = 2
}
public enum ProfileRoles
{
BaseProfile = 0,
ConcreteProfile1 = 1,
ConcreteProfile2 = 2
}
And It needs to be configured well in the DbContext
:
public class UserStoreDbContext : DbContext
{
DbSet<User> Users { get; set; }
DbSet<UserProfile> Profiles { get; set; }
public UserStoreDbContext(DbContextOptions<UserStoreDbContext> options) : base(options) { }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
base.OnModelCreating(modelBuilder);
modelBuilder.Entity<User>(builder =>
{
builder.HasDiscriminator<UserRoles>(x => x.Role)
.HasValue<User>(Roles.User)
.HasValue<Logistian>(Roles.ConcreteUser1)
.HasValue<Driver>(Roles.ConcreteUser2)
});
modelBuilder.Entity<UserProfile>(builder =>
{
builder.HasDiscriminator<ProfileRoles>(x => x.Role)
.HasValue<UserProfile>(ProfileRoles.BaseProfile)
.HasValue<ConcreteUser1Profile>(Roles.ConcreteProfile1)
.HasValue<ConcreteUser2Profile>(Roles.ConcreteProfile2)
});
}
}
In this case when you will use the code like: dbContext.Users.FirstOrDefault()
, you also will get correct derived profile from UserProfile
.
Maybe someone will need this solution too.
You can use HasDiscriminator
on model creating.
It will use a field to be able to decide which concrete user class you're trying map the data to.
For you it'll be something like:
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<User>()
.HasDiscriminator<int>("UserType")
.HasValue<ConcreteUser1>(1)
.HasValue<ConcreteUser2>(2);
}
More at: https://www.learnentityframeworkcore.com/configuration/fluent-api/hasdiscriminator-method