同一会话更新的 NHibernate 级联行为
有关 NHibernate 级联设置的文档讨论了调用 Save()、Update() 和 Delete() 方法的上下文中的设置。但我找不到在隐式更新上下文中级联行为的讨论,隐式更新是在同一会话上加载、修改和保存实体时发生的。在这种情况下,不需要显式调用更新,那么级联设置会发生什么情况呢?
这似乎是一个愚蠢的问题,但我提出这个问题的原因是我试图弄清楚 NHibernate 如何在领域驱动设计的背景下支持聚合边界的概念。让我举一个例子来说明我想要表达的意思。
假设我有一个包含实体 Invoice、Buyer 和 LineItem 的规范发票应用程序。 Invoice 是聚合根,LineItem 位于同一聚合中,但 Buyer 是其自己的聚合根。
我想在 NHibernate 中对此进行建模,方法是配置映射,以便从 Invoice 到 LineItem 的级联为 All-DeleteOrphans
,从 Invoice 到 Buyer 的级联为 None
。
根据我读过的文档,使用我想要的级联设置,如果我正在处理断开连接的实体并且执行以下操作,则只有发票和行项目将保存:
disconnectedInvoice.ShippedDate = DateTime.Today();
disconnectedInvoice.LineItems[2].Backordered = true;
disconnectedInvoice.Buyer.Address = buyersNewAddress;
session.Update(disconnectedInvoice);
session.Flush();
我在任何地方都没有看到讨论的是当人们检索发票,进行相同的更新并以连接的方式刷新会话,就像这样。
var invoice = session.Get<Invoice>(invoiceNumber);
invoice.ShippedDate = DateTime.Today();
invoice.LineItems[2].Backordered = true;
invoice.Buyer.Address = buyersNewAddress;
session.Flush();
NHibernate 文档说刷新会保留与会话关联的脏实体的更改。基于这一点,我们假设发票、买方和订单项的更新都将被保留。
然而,这似乎违反了级联规则背后的概念。在我看来,为了决定刷新时更新哪些实体,会话应该查看那些直接加载的实体(本例中仅是发票)并包括间接加载的实体(本例中的 LineItems 和 Buyer)仅当级联设置指示应保留它们时。
我承认这个例子代表了糟糕的 DDD。如果买家不属于聚合的一部分,那么此时不应更新。或者至少不应该通过发票聚合进行更新。然而,除了 DDD 之外,我实际上更感兴趣的是确定级联规则是否适用于同一会话场景中的更新,就像它们在断开连接的场景中一样。
Documentation on NHibernate cascade settings, discusses the settings in the context of calling the Save() Update() and Delete() methods. But I can find no discussion of cascade behavior in the context of the implicit update that occurs when one loads, modifies and saves entities on the same session. In this case an explicit call to update is not needed, so what happens regarding cascade settings?
This may seem a dumb question, but the reason I bring this up is that I am trying to figure out how NHibernate supports the concept of Aggregate Boundaries in the context of Domain Driven Design. Let me give an example that will illustrate what I am trying to get at.
Suppose I have the canonical invoice application with the entities Invoice, Buyer and LineItem. Invoice is an aggregate root and LineItem is in the same aggregate but Buyer is its own aggregate root.
I want to model this in NHibernate by configuring my mapping such that the cascade from Invoice to LineItem is All-DeleteOrphans
and the one from Invoice to Buyer is None
.
Based on the documentation I have read, using my desired cascade settings, if I am working with disconnected entities and I do the following, only the Invoice and LineItems will save:
disconnectedInvoice.ShippedDate = DateTime.Today();
disconnectedInvoice.LineItems[2].Backordered = true;
disconnectedInvoice.Buyer.Address = buyersNewAddress;
session.Update(disconnectedInvoice);
session.Flush();
What I don't see discussed anywhere is what happens when one retrieves the invoice, makes the same updates and flushes the session in a connected manner like so.
var invoice = session.Get<Invoice>(invoiceNumber);
invoice.ShippedDate = DateTime.Today();
invoice.LineItems[2].Backordered = true;
invoice.Buyer.Address = buyersNewAddress;
session.Flush();
The NHibernate documentation says that flush persists the changes for dirty entities associated with the session. Based on this one would presume that the updates to the Invoice, the Buyer and the LineItems will all be persisted.
However, this would seem to violate the concept behind the cascade rule. It would seem to me that for the purposes of deciding what entities to update upon flush, the session should look at those entities that were directly loaded (only the invoice in this case) and include indirectly loaded entities (the LineItems and the Buyer in this case) only if the cascade setting indicates they should be persisted.
I admit that this example represents bad DDD. If the Buyer isn't part of the aggregate then it should not be being updated at this time. Or at least it should not be being updated through the Invoice aggregate. However, DDD aside, the point I am actually more interested in is determining if cascade rules are honored for updates in the same-session scenario the same as they are in the disconnected scenario.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(1)
主要问题是断开连接的实体和连接的实体之间的差异。两者的级联行为相同,但不同的是隐式更新。对于会话加载的实体,无需将保存级联到买方,因为它是多余的。对于断开连接的实体,您需要级联,因为该买家不会有隐式更新,因为它从未显式合并到会话中。
The main issue is the difference between disconnected and connected entities. Cascading behaves the same for both, but it's the implicit updates that are different. For session loaded entities, there is no need to cascade the save to the Buyer since it'd be redundant. For a disconnected entity, you need the cascade since there will be no implicit updates to that Buyer because it was never explicitly merged into the session.