Data property inheritance in EF Core 3.0

.net-core c# entity-framework-core

Question

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?

1
0
11/1/2019 2:39:56 PM

Accepted Answer

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.

0
11/9/2019 3:54:54 PM

Popular Answer

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



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow