One-to-one with different classes in EF Core 2.2

asp.net-core c# ef-fluent-api entity-framework-core

Question

I'd like to follow this blog explaining how to configure one-to-one relationship. Its idea is that one entity gets a property of the other's type while the other one gets a property of the former's type plus an ID to it to create a foreign key.

My issue is, though, that I want to brake out the contact part of two different classes like so. The class SomeThing is already refactored and works well with the class Address. However, I'm not sure how to deal with the class SomeThingElse.

public class SomeThing
{
  public Guid Id { get; set; }
  //public string Street { get; set; }
  //public string City { get; set; }
  public Address Address { get; set; }
}

public class Address
{
  public Guid Id { get; set; }
  public string Street { get; set; }
  public string City { get; set; }
  public Guid SomeThingId { get; set; }
  public SomeThing SomeThing { get; set; }  
}

public class SomeThingElse
{
  public Guid Id { get; set; }
  public string Street { get; set; }
  public string City { get; set; }
  //public Address Address { get; set; }
}

I've tried adding a specialized class for managing the address of SomeThingElse but then, it makes no sense to break it out. I considered adding the two fields below but rejected the idea as poor design for the DB.

public class Address
{
  ...
  public Guid SomeThingElseId { get; set; }
  public SomeThingElse SomeThingElse { get; set; }  
}

Preferably, this is a school book case for inheritance introducing a base class Contactable and skipping Address altogether. But I recall from before that inheritance and EF don't mix well and that there's a lot of oopsies and gotchas to be expected in such case.

Is there a reliable best-practice for doing that? I haven't found anything that felt trustable enough when I googled.

1
0
2/3/2019 3:42:11 AM

Accepted Answer

As from the discussion in the comments, I am going into a details answer:

You can use EF Core newly introduced Owned Entity type feature where Address is the Owned Entity type of Something and SomethingElse while Something and SomethingElse are the owners as follows:

modelBuilder.Entity<SomeThing>().OwnsOne(st => st.Address);
modelBuilder.Entity<SomeThingElse>().OwnsOne(st => st.Address);

By convention, EF Core will name the database columns for the properties of the owned entity type following the pattern Navigation_OwnedEntityProperty. Therefore the Address properties will appear in the Something and SomethingElse table with the names 'Address_Street' and 'Address_City'.

Now if you don't want owned entity type column name to be like Navigation_OwnedEntityProperty then you can give your custom column name as follows:

modelBuilder.Entity<SomeThing>().OwnsOne(st => st.Address,
     a =>
     {
          a.Property(p => p.Street).HasColumnName("Street");
          a.Property(p => p.City).HasColumnName("City");
     });

modelBuilder.Entity<SomeThingElse>().OwnsOne(ste => ste.Address,
     a =>
     {
          a.Property(p => p.Street).HasColumnName("Street");
          a.Property(p => p.City).HasColumnName("City");
     });

Moreover owned types can be stored in a separate table from the owner. In order to override the convention that maps an owned type to the same table as the owner, you can simply call ToTable and provide a different table name as follows:

modelBuilder.Entity<SomeThing>().OwnsOne(st => st.Address,
      a =>
      {
            a.ToTable("SomeThingAddress");
      });

 modelBuilder.Entity<SomeThingElse>().OwnsOne(ste => ste.Address,
      a =>
      {
          a.ToTable("SomeThingElseAddress");
      });

Querying owned types

When querying the owner the owned types will be included by default. It is not necessary to use the Include method, even if the owned types are stored in a separate table.

Limitations

Some of these limitations are fundamental to how owned entity types work, but some others are restrictions that we may be able to remove in future releases:

By-design restrictions:

  • You cannot create a DbSet<T> for an owned type
  • You cannot call Entity<T>() with an owned type on ModelBuilder

For more details: EF Core Owned Entity Types Limitations

3
2/3/2019 3:12:36 AM


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