【问题标题】:Aggregate Root support in Entity Framework实体框架中的聚合根支持
【发布时间】:2012-06-18 16:47:54
【问题描述】:

我们如何告诉实体框架Aggregates?

  1. 保存聚合时,将实体保存在聚合中
  2. 删除聚合时,删除聚合中的实体
  3. 当两个不同的用户尝试修改同一聚合中的两个不同实体时引发并发错误
  4. 加载聚合时,即使在我们访问聚合中的所有实体之前存在一些时间延迟,也要提供聚合的一致时间点视图

(实体框架 4.3.1 代码优先)

【问题讨论】:

  • 你想要的是 Event Sourcing cqrs.wordpress.com/documents/events-as-storage-mechanism 。 EF 在这里几乎没用,只是昂贵的开销
  • 谢谢。不幸的是,我们不在一个可以接受事件溯源的环境中。
  • 在领域驱动设计应用程序中使用 EF 两年后:EF 被称为“实体框架”而不是“聚合根框架”是有原因的。

标签: entity-framework domain-driven-design aggregateroot


【解决方案1】:

EF 提供了允许您定义和使用聚合的功能:

  1. 这是最痛苦的部分。 EF 使用实体图。如果您有像 Invoice 这样的实体,并且该实体具有相关 InvoiceLine 实体的集合,您可以像聚合一样处理它。如果您在附加场景中,一切都按预期工作,但在分离场景中(EF 未加载聚合或由不同的上下文实例加载),您必须将聚合附加到上下文实例并准确告诉它您更改了什么 = 设置状态对象图中的每个实体和独立关联。
  2. 这由级联删除处理 - 如果您加载了相关实体,EF 将删除它们,但如果您不这样做,您必须在数据库中的关系上配置级联删除。
  3. 这由数据库中的并发令牌处理 - 最常见的是时间戳或行版本列。
  4. 您必须使用预先加载并在开始时一起加载所有数据(= 一致的观点),或者您将使用延迟加载,在这种情况下您将不会有一致的观点,因为延迟加载将加载当前状态关系,但它不会更新您已经加载的聚合的其他部分(如果您尝试使用 EF 实现这种刷新,我认为这是性能杀手)。

【讨论】:

  • #3 - 当两个用户在同一张发票上修改不同的 InvoiceLine 时,EF 会检测到冲突吗? #4 - 在每个查询上使用 .Include() 进行预加载很痛苦并且容易出错
  • #3 在这种情况下,您必须确保更改 InvoiceLine 会将 Invoice 设置为修改状态,并且将执行发票更新。
  • #4 急切加载在编码时间可能会很痛苦,但它是明确的,它显示了实际发生的情况。您始终可以重构代码以使用将定义预加载的通用查询库。使用延迟加载实现一致的观点需要从聚合中一致地重新查询所有加载的数据。延迟加载可能很糟糕,但这将非常糟糕。
  • 谢谢拉迪斯拉夫。听起来我们可以使用 EF 对真正的聚合进行建模,尽管我会说它不受直接支持。
  • #4 - 我想说这并不明确,因为我们正在复制有关聚合到子实体关系的信息。如果您通过延迟加载属性导航到另一个聚合,.Include() 也无济于事,除非查询急切加载调用者将使用的每个聚合和可导航聚合。
【解决方案2】:

我为此专门写了GraphDiff。它允许您通过提供流畅的映射来定义更新时的“聚合边界”。我在需要来回传递分离的实体图的情况下使用它。

例如:

// Update method of repository
public void Update(Order order)
{
    context.UpdateGraph(order, map => map
        .OwnedCollection(p => p.OrderItems);
}

上面将告诉实体框架更新订单实体并合并 OrderItems 的集合。以这种方式映射允许我们确保实体框架仅在我们在聚合上定义的范围内管理图形并忽略所有其他属性。它支持对所有实体的乐观并发检查。它可以处理更复杂的场景,还可以处理多对多场景中的更新引用(通过 AssociatedCollections)。

希望这个有用。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-09-06
    相关资源
    最近更新 更多