首先是EF代码:如何在遵循DDD的同时从实体's Collection中删除一行
本文关键字:Collection 实体 删除 一行 代码 EF DDD | 更新日期: 2023-09-27 18:16:43
情况是这样的:
DDD指出,您使用存储库来获取聚合根,然后使用它来添加/删除它所拥有的任何集合。
添加很简单,您只需在希望添加到的Collection
上调用.Add(Item item)
。保存时将向数据库中添加新行。然而,删除是不同的-调用.Remove(Item item)
不会从数据库中删除项目,它只是删除外键。所以,虽然,是的,从技术上讲,它不再是集合的一部分了,但它仍然在数据库中。
读取周围,唯一的解决方案是使用数据上下文删除它。但是根据DDD,域对象不应该知道数据上下文,因此删除必须在域外完成。
正确的做法是什么?或者让数据库中满是孤儿是可以接受的(也许运行一个例程来清除它们)?
我已经解决了这个问题在应用程序中,我目前正在使用域事件;埃里克·埃文斯说,DDD的概念应该出现在他的书里。
虽然域对象不允许知道对象上下文,但IDomainEventHandler
是-因此,我有一个DomainObjectDeletionHandler
,它在控制返回到我的应用层之前从对象上下文中删除"已删除"对象,并保存更改。
关于更多信息,我写了一篇关于我实现域事件以及如何将所有事情联系在一起的博客。
希望有帮助:)
编辑
例如,如果你有一个Order
类,它有一个OrderItems
集合类型为OrderItem
:
public class Order
{
// Other stuff
public void RemoveOrderItem(int orderItemId)
{
var orderItemToRemove = OrderItems.First(oi => oi.Id == orderItemId)
OrderItems.Remove(orderItemToRemove);
DomainEvents.Raise(new OrderItemRemoved(orderItemToRemove));
}
}
当从集合中删除子实体时,EF将其保留为孤儿,仅删除外键。
如果您不想使用DbContext显式地删除它,您可以使用所谓的"识别关系"(http://msdn.microsoft.com/en-us/library/ee373856.aspx在底部)。
诀窍是在子节点上设置一个复合主键,包括父节点的主键。
一旦这样做,当从父集合中删除实体时,它也将从表中删除。
我不知道这是否是设计的,但是如果一个详细对象有一个包含其主对象的键列的复合键,那么如果从主对象的集合中删除它,它将被自动删除。如果你有一个带有OrderID键和ICollection OrderLines导航属性的Order对象,给OrderLine一个包含OrderID和OrderLineID的复合键。
但是因为我不知道我是否可以依赖它,我自己使用的解决方案是让EF按照它的方式处理它,并在调用SaveChanges()时修复"分离的"(不是EF术语)详细对象,枚举所有修改的实体并将状态更改为适当的删除
我通过根据需要配置引用列并将删除行为配置为Cascade
来解决此场景
的例子:
modelBuilder.Entity<AggregateRoot>()
.HasMany(x => x.Items)
.WithOne()
.IsRequired()
.OnDelete(DeleteBehavior.Cascade);
在这种情况下,EF Core (6.x)不再将引用列设置为NULL
,而是通过从聚合根的Items
集合中删除Item
来删除记录。
此处的决定性配置是删除行为Cascade
。
为什么不使用两个存储库?
var parent = ParentRepo.Get(parentId);
parent.Children.Remove(childId); // remove it from the property Collection
ChildRepo.Delete(childId); // delete it from the database
ParentRepo.Commit(); // calls underlying context.SaveChanges()
假设您通过IOC/DI共享上下文,对一个repo调用commit将同时提交两个上下文,否则只调用ChildRepo.Commit
。