EF Core entity inheritance from different sources without discriminator

asp.net asp.net-core c# entity-framework entity-framework-core

Question

I am trying to configure the two entities shown below with EF Core:

User:

public class User
{
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    [Required]
    public string Email { get; set; }

    [NotMapped]
    public string FriendlyIdentifier
    {
        get
        {
            var name = $"{Name} {Surname}";
            if (!string.IsNullOrWhiteSpace(name))
                return name;
            else if (!string.IsNullOrWhiteSpace(Email))
                return Email;
            else
                return UserId.ToString();
        }
    }

    public ICollection<Booking> Bookings { get; set; }
}

UserDetails:

public class UserDetails : User
{
    public DateTime? LatestBookingDate { get; set; }
}

The reason for this is that I want to be able to pull users with their latest booking date without having to pull all the booking and then determine the latest booking date manually in code. To achieve this I have created a SQL view which selects the latest booking date and I want to bind UserDetails to this view.

I have the following EF setup:

modelBuilder.Entity<User>(builder =>
{
    builder.ToTable("userdetails");
    builder.HasKey(x => x.UserId);
    builder.HasIndex(x => new { x.Name, x.Surname, x.Email });
});

modelBuilder.Query<UserDetails>()
    .ToView("view_userdetails");

But this gives the following error when trying to select a UserDetails entity:

'Cannot set 'User' as the base type of 'UserDetails'. Inheritance hierarchies cannot contain a mix of entity types and query types.'

I am aware that there is a HasDiscriminator extension as described here: https://docs.microsoft.com/en-us/ef/core/modeling/relational/inheritance but I don't really have a discriminator as UserDetails is just an extension of User.

Is there any way for me to get this working? I don't really want to duplicate the properties of User into UserDetails because then there's two entities to maintain when anything changes. I would like to retain the inheritance.

1
3
12/7/2018 4:07:37 PM

Accepted Answer

I would like to retain the inheritance.

You can't (at least not in the way it is now). That's clearly indicated by the exception message - it says "cannot contain a mix" and does not provide you a suggestion.

What you can do is to move your current User class members to a new base non entity, non query type class and let both User entity and UserDetails query type inherit from it, e.g.:

public abstract class UserInfo
{
    public int UserId { get; set; }
    public string Title { get; set; }
    public string Name { get; set; }
    public string Surname { get; set; }
    [Required]
    public string Email { get; set; }

    [NotMapped]
    public string FriendlyIdentifier
    {
        get
        {
            var name = $"{Name} {Surname}";
            if (!string.IsNullOrWhiteSpace(name))
                return name;
            else if (!string.IsNullOrWhiteSpace(Email))
                return Email;
            else
                return UserId.ToString();
        }
    }

    public ICollection<Booking> Bookings { get; set; }
}

public class User : UserInfo { }

public class UserDetails : UserInfo
{
    public DateTime? LatestBookingDate { get; set; }
}

This will allow you to maintain the common model in a single place. The drawback is that UserDetails will not be able to be used in a places which expect User, and has to be converted (although since all common properties are the same, it should be trivial "auto" task for AutoMapper).

Please note that this solution requires you to Ignore the Bookings navigation property in the query type:

modelBuilder.Query<UserDetails>(builder =>
{ 
    builder.ToView("view_userdetails");
    builder.Ignore(e => e.Bookings);
});

or move it to the User entity.

8
12/7/2018 4:53:21 PM


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