在 EF Core 2.2 中与不同 类 一对一

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

我想按照this blog解释如何配置一对一关系。它的想法是,一个实体获得另一个类型的 属性,而另一个实体获得前者类型的 属性 加上它的 ID 以创建外键。

不过,我的问题是,我想像这样断开两个不同 classes 的接触部分。 class SomeThing 已经重构并且与 class Address 一起工作得很好。但是,我不确定如何处理 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; }
}

我已经尝试添加一个专门的 class 来管理 SomeThingElse 的地址,但是,将其分解是没有意义的。我考虑过添加下面的两个字段,但拒绝了这个想法,因为数据库设计不佳。

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

最好是,这是一个关于继承的教科书案例,它引入了基数 class Contactable 并完全跳过了 Address。但我记得以前继承和 EF 不能很好地混合,在这种情况下会出现很多错误和陷阱。

是否有可靠的最佳实践来做到这一点?当我用谷歌搜索时,我没有找到任何值得信赖的东西。

根据评论中的讨论,我将进入详细答案:

您可以使用 EF Core 新引入的 Owned Entity 类型功能,其中 AddressSomethingOwned Entity 类型,并且SomethingElseSomethingSomethingElse 的所有者如下:

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

按照惯例,EF Core 将按照模式 Navigation_OwnedEntityProperty 为拥有的实体类型的属性命名数据库列。因此 Address 属性将出现在 SomethingSomethingElse table 中,名称为 'Address_Street' 和 'Address_City'.

现在,如果您不希望拥有的实体类型列名称类似于 Navigation_OwnedEntityProperty,那么您可以按如下方式提供自定义列名称:

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");
     });

此外,拥有的类型可以存储在与所有者分开的 table 中。为了覆盖将拥有的类型映射到与所有者相同的 table 的约定,您可以简单地调用 ToTable 并提供不同的 table 名称,如下所示:

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

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

查询拥有的类型

当查询所有者时,默认情况下将包括拥有的类型。没有必要使用 Include 方法,即使拥有的类型存储在单独的 table.

中也是如此

限制

其中一些限制是自有实体类型工作方式的基础,但其他一些限制我们可能会在未来的版本中删除:

设计限制:

  • 您不能为拥有的类型创建 DbSet<T>
  • 您不能在 ModelBuilder
  • 上使用拥有的类型调用 Entity<T>()

更多详情:EF Core Owned Entity Types Limitations