Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EF Core 2.2, owned entities generated as another table when multiple in hierarchy

I have a model with a class Address marked [Owned] and a hierarchy of people (person, customer or employee, then even more subtypes etc). There are addresses at different stages of this hierarchy and all of it ends up in one table as EF Core is limited to table per hierarchy. I expected all the attributes from address to appear multiple times in that person table (once per mention in any of the subtypes) however it doesn't appear at all! Instead i see FK for each of them and a separate Address table.

Does EF Core not support multiple owned members of the same type? If not is there anything i should do? I don't have any fluent API / specific configuration that could interfere with the defaults (new empty console project, only config line is .UseSQLServer(connectionstring)

Sample code bellow :

public class SampleContext : DbContext
{
    public virtual DbSet<Address> Addresses { get; set; }
    public virtual DbSet<Customer> Customers { get; set; }
    public virtual DbSet<Employee> Employees { get; set; }
    public virtual DbSet<Person> Persons { get; set; }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        if (!optionsBuilder.IsConfigured)
        {
            optionsBuilder.UseSqlServer("my connection string here");
        }
        base.OnConfiguring(optionsBuilder);
    }
}
[Owned]
public class Address
{
    public int Id { get; set; }
    public string AddressLine1 { get; set; }
    public string AddressLine2 { get; set; }
    public string AddressLine3 { get; set; }
    public string City { get; set; }
}

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }
}

public class Employee : Person
{
    public Address Address { get; set; }
}

public class Customer : Person
{
    public Address DeliveryAddress { get; set; }
    public Address InvoicingAddress { get; set; }
}

Expected Person table :

DeliveryAddressAddressLine1
DeliveryAddressAddressLine2
DeliveryAddressAddressLine3
DeliveryAddressAddressCity
InvoicingAddressAddressLine1
InvoicingAddressAddressLine2
InvoicingAddressAddressLine3
InvoicingAddressAddressCity
EmployeeAddressAddressLine1
EmployeeAddressAddressLine2
EmployeeAddressAddressLine3
EmployeeAddressAddressCity

Generated Person table (+ an unexpected Address table):

EmployeeAddressAddressId
DeliveryAddressAddressId
InvoicingAddressAddressId

Edit : updated the question, added the context definition and noticed i had Addresses as a DbSet so i assume this may be the cause, removing it gives me the following error :

Cannot use table 'Person' for entity type 'Customer.DeliveryAddress#Address' since it is being used for entity type 'Employee.Address#Address' and there is no relationship between their primary keys.`

like image 916
Ronan Thibaudau Avatar asked Feb 04 '19 02:02

Ronan Thibaudau


1 Answers

According to EF Core Owned Entity Types documentation:

Inheritance hierarchies that include owned entity types are not supported

You can overcome this problem by moving public Address Address { get; set; }, public Address DeliveryAddress { get; set; } and public Address InvoicingAddress { get; set; } navigation properties from Employee and Customer to the base class Person as follows:

public class Person
{
    public int Id { get; set; }
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public DateTime DateOfBirth { get; set; }

    public Address Address { get; set; }
    public Address DeliveryAddress { get; set; }
    public Address InvoicingAddress { get; set; }
}

Then configure with fluent API to override the Navigation_OwnedEntityProperty rule for owned entity column name as follows:

modelBuilder.Entity<Person>().OwnsOne(p => p.Address,
    a =>
    {
         a.Property(p => p.AddressLine1).HasColumnName("EmployeeAddressLine1");
         a.Property(p => p.AddressLine2).HasColumnName("EmployeeAddressLine2");
         a.Property(p => p.AddressLine2).HasColumnName("EmployeeAddressLine3");
         a.Property(p => p.City).HasColumnName("EmployeeAddressCity");
    }).OwnsOne(p => p.DeliveryAddress,
    a =>
    {
        a.Property(p => p.AddressLine1).HasColumnName("DeliveryAddressLine1");
        a.Property(p => p.AddressLine2).HasColumnName("DeliveryAddressLine2");
        a.Property(p => p.AddressLine2).HasColumnName("DeliveryAddressLine3");
        a.Property(p => p.City).HasColumnName("DeliveryAddressCity");
   }).OwnsOne(p => p.InvoicingAddress,
   a =>
   {
        a.Property(p => p.AddressLine1).HasColumnName("InvoicingAddressLine1");
        a.Property(p => p.AddressLine2).HasColumnName("InvoicingAddressLine2");
        a.Property(p => p.AddressLine2).HasColumnName("InvoicingAddressLine3");
        a.Property(p => p.City).HasColumnName("InvoicingAddressCity");
   });

Now you if you don't want to move public Address Address { get; set; }, public Address DeliveryAddress { get; set; } and public Address InvoicingAddress { get; set; } navigation properties from Employee and Customer to the base class Person then you have to create separate tables from each address types as follows:

modelBuilder.Entity<Employee>().OwnsOne(p => p.Address,
    a =>
    {
        a.ToTable("EmployeeAddresses");
    });

modelBuilder.Entity<Customer>().OwnsOne(p => p.DeliveryAddress,
    a =>
    {
        a.ToTable("DeliveryAddresses");
    }).OwnsOne(p => p.InvoicingAddress,
    a =>
    {
        a.ToTable("InvoicingAddresses");
    });
like image 146
TanvirArjel Avatar answered Nov 23 '22 05:11

TanvirArjel