【问题标题】:EF 6: mapping complex type collection?EF 6:映射复杂类型集合?
【发布时间】:2015-07-04 21:58:29
【问题描述】:

EF 6(代码优先)是否支持复杂类型集合(值对象集合)映射?我知道它支持复杂类型,但还没有找到我们拥有复杂类型集合的示例。

例如,假设您有一个名为 Student 的实体,它有一个联系人集合。对于 NH,我可以简单地说 Student 有一个联系人集合,并且该联系人是一个组件(相当于 ef 中的复杂类型)。这可以在不更改与实体的联系的情况下使用 EF 完成吗?

【问题讨论】:

  • 我不知道 NH,但在 EF 中,您可以声明一个 X-to-X 关系,它可以满足您的需求,是的。不过,我不确定您所说的“不改变与实体的联系”是什么意思。
  • 你好杰罗恩。我的理解是,当您在使用 EF 时将对象映射到表时,您需要指定主键,对吧?我不想对 Contact 这样做,因为正如我所说,它表现为一个值对象,而不是实体
  • 很遗憾,我相信答案是否定的……
  • 它是另一个具有外键的表,并且 nh 不强制映射值类型的主键(这就是它被称为值类型的原因!)
  • 我希望他们这样做...同时,我会继续使用 NHibernate...

标签: c# entity-framework domain-driven-design complextype value-objects


【解决方案1】:

只需为复杂类型中的每个属性添加一列,即可将复杂类型映射到实体表。因此,将联系人作为一个单独的实体:

public class Student
{
    [Key]
    public int ID {get; set;}
    public Contact PrimaryContact { get; set; }
    public Contact SecondaryContact{ get; set; }
}

[ComplexType]
public class Contact
{
    public string Address{get; set;}
    public string PhoneNumber{get; set;}
    public string Email{get; set;}
}

将生成包含列的 Student 表的映射:

ID
PrimaryContact_Address
PrimaryContact_PhoneNumber
PrimaryContact_Email
SecondaryContact_Address
SecondaryContact_PhoneNumber
SecondaryContact_Email

复杂类型只是在需要时声明相同成员的简写。您可以在一堆需要联系人数据的其他实体中使用相同的联系人类型,而无需为每个实体显式定义AddressPhoneNumberEmail 属性。所以你不能真正在集合中使用它们,因为每次你想向它添加或删除项目时,你都必须从表本身添加或删除列。

public class Student
{
    [Key]
    public int ID {get; set;}
    public ICollection<Contact> Contacts{ get; set; }
}

[ComplexType]
public class Contact
{
    public string Address{get; set;}
    public string PhoneNumber{get; set;}
    public string Email{get; set;}
}

ID
Contacts[x]_Address?
Contacts[x]_PhoneNumber?
Contacts[x]_Email?

联系人项目实际存储在哪里?你怎么能索引它?如果您尝试这样做,映射器将完全忽略 Contacts 属性。

您可以通过从 Contact 类中删除 ComplexType 来干净地使用集合。不过,这会在数据库中创建一个表:

public class Student
{
    [Key]
    public int ID {get; set;}
    public ICollection<Contact> Contacts{ get; set; }
}

public class Contact
{
    [Key]
    public int ID {get; set;}
    public string Address{get; set;}
    public string PhoneNumber{get; set;}
    public string Email{get; set;}
}

【讨论】:

  • 将类型映射到表是 ORM 的职责。遗憾的是,EF 大量泄漏到现有域中。关于您的问题,不确定这里有什么困惑。如果 A 是具有引用 B 集合的属性的类型,而 B 是值对象(复杂类型),那么显然 B 甚至不需要跟踪其对应表的 id,对吗? NH 做得很好 (nhibernate.info/doc/nhibernate-reference/components.html)...
  • 它们是反问句。 :) 他们根本无法回答并说明为什么您不能拥有复杂类型的集合。要执行您要求的操作,您需要创建一个单独的联系人实体以映射到表以存储每个项目。您链接的那个页面只显示了如何在复合元素中拥有集合,而不是如何拥有 OF 复合元素的集合。
  • 事实上,一般情况下你不能有值类型的集合。您可以将序列化集合存储在单个属性中,但就纯度而言,这将是荒谬的。您可以在 A 实体中拥有 B 实体的集合,因为每个 B 项目都作为具有自己 ID 的单独行存储在 B 的表中。该 ID 是存储在 A 中的内容。该页面仅提及复合元素内的集合,而不是复合元素的集合。我对 NHibernate 一无所知,但它也必须有一个地方可以将 B 的项目也放在一个单独的表中。这将与另一个 EF 实体相同。
  • 第 7.2 节讨论了它。是的,b 被保存到另一个表中,但它的跟踪与实体完全不同,因为它是一个值对象!
【解决方案2】:

显然 NHibernate 在这方面更灵活,因为在撰写本文时(EF6.2 和 EF Core 2.1),EF6 和 EF Core 都不支持复杂(或更一般地是原始或值对象)类型集合映射。

EF Core 更糟糕,因为 Owned Entity Types,据说是 EF 复杂类型的替代品,实际上具有更多类似实体的行为(从更改跟踪的角度来看),并且不能用作 DDD 不可变多属性值对象.

我知道的唯一解决方法是将值对象/集合表示以某种序列化格式((例如 XML、JSON、二进制等)映射到单个数据库字符串/二进制列。虽然这适用于读取/存储数据,它缺乏查询能力,所以 IMO 不是一个严肃的选择。

谈到 EF6,我认为它永远不会得到这种支持。 EF6基本处于维护模式,won't get future major improvements

EF Core 在这方面看起来更有希望,因为对拥有实体集合的支持scheduled for the next EF 2.2 release。但是不知道他们将如何(最初)实现它们,并且考虑到他们是如何实现拥有的类型的,这可能不是您期望的方式,所以如果他们不能提前说适用于值对象收集场景。

我知道这不是你想要的答案,但这就是现实。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-03-17
    • 2020-05-03
    • 1970-01-01
    • 1970-01-01
    • 2012-05-30
    • 2016-08-04
    • 2011-08-05
    • 1970-01-01
    相关资源
    最近更新 更多