EF代码优先:如何在跟踪DDD时从实体的Collection中删除一行?

时间:2011-12-02 17:29:38

标签: c# entity-framework domain-driven-design code-first

所以这是场景:

DDD声明您使用存储库来获取聚合根,然后使用它来添加/删除它所拥有的任何集合。

添加很简单,只需在要添加的.Add(Item item)上调用Collection即可。保存时,会在数据库中添加一个新行。但是,删除是不同的 - 调用.Remove(Item item)不会从数据库中删除该项,它只是删除外键。所以,虽然,是的,它在技术上不再是集合的一部分,它仍然在数据库中。

阅读,唯一的解决方案是使用数据上下文删除它。但根据DDD,域对象不应该知道数据上下文,因此删除必须在域外进行。

正确的方法是什么?或者让数据库中的孤儿都可以接受(也许正在运行例程来清除它们)?

4 个答案:

答案 0 :(得分:11)

我已经使用domain events在我正在处理的应用程序中解决了这个问题; DDD概念Eric Evans said应该出现在他的书中。

虽然不允许域对象知道对象上下文,但IDomainEventHandler是 - 我因此得到一个DomainObjectDeletionHandler,它在控制返回之前从对象上下文中删除'已删除'对象我的应用程序层和更改已保存。

有关更多信息,我写了a blog关于我的域事件实现以及我如何将所有内容连接在一起。

希望有所帮助:)

修改

例如,如果您的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));
    }
}

答案 1 :(得分:5)

从集合中删除子实体时,EF会将其保留为孤儿,只删除外键。

如果您不想使用DbContext显式删除它,您可以使用所谓的“识别关系”(底部为http://msdn.microsoft.com/en-us/library/ee373856.aspx)。

诀窍是在子项上设置复合主键,包括父键的主键。

一旦你这样做,当从父集合中删除实体时,它也将从表中删除。

答案 2 :(得分:3)

我不知道这是否是设计的,但如果详细信息对象具有包含其主对象的键列的复合键,则如果从主对象中删除它,它将自动删除。采集。如果您有一个带有OrderID键和ICollection OrderLines导航属性的Order对象,请为OrderLine提供一个包含OrderID和OrderLineID的复合键。

但是由于我不知道我是否可以依赖它,我自己使用的解决方案是让EF按照它的方式处理它,然后修复分离的问题' (不是EF术语)调用SaveChanges()时的详细对象,枚举所有已修改的实体并根据需要将状态更改为已删除。

答案 3 :(得分:0)

为什么不使用两个存储库?

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