22

我们首先使用具有外键关系的实体框架代码。我们研究了在我们的应用程序中处理从实体 ICollection 中删除对象的方法。

当我们有一个具有子关系的实体时,我们可以使用 Add 方法将对象直接添加到它们的 ICollection 中。现在,当您使用 remove 时,您会收到错误消息

System.InvalidOperationException 发生消息=操作失败:由于一个或多个外键属性不可为空,无法更改关系。当对关系进行更改时,相关的外键属性将设置为空值。如果外键不支持空值,则必须定义新关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。

我理解这是因为集合上的 Remove 仅通过使外键为空来删除关系。我们想在实体中编写业务逻辑并允许删除。

因此,从它的存储库中获取根实体,例如从 OrderRepository 中的订单,然后调用实体的某些特定方法,例如,Order.AddOrderline(Orderline orderline)这会在订单中添加一个 OrderLinevirtual ICollection<OrderLine> OrderLines

但是,我们不能编写这样的代码,Order.CancelOrderline(int orderLineId)因为简单地从 ICollection 中删除会导致储蓄更改出错。

无论如何,仅通过操作对象集合似乎无法实现这一目标。显然我们可以直接从 Context 中移除。但是我想让它成为实体的一部分。我们可以在实体框架的 SaveChanges 事件中清理某些没有外键的实体吗?显然需要告诉 EF 如果它们具有空外键,哪些实体可以被删除。

我们目前正在使用存储库模式,因此控制器无法访问上下文。我显然可以在 Order 存储库上使用 OrderLine 存储库或 remove OrderLine 方法。但是只是想知道是否可以在不引用持久性机制的情况下在实体上编写代码。

想法?我们做这一切都错了吗?其他 ORM 是否允许您从子集合中删除?

4

2 回答 2

40

我不知道以下是否适合您,但实体框架支持识别关系。在这种关系中,子实体(从属)到父(主)实体的外键必须是子实体(复合)主键的一部分。例如 - 使用DbContext数据注释 - 您的模型类必须如下所示:

public class Order
{
    [Key]
    public int OrderId { get; set; }

    public ICollection<OrderLine> OrderLines { get; set; }
}

public class OrderLine
{
    [Key, ForeignKey("Order"), Column(Order = 1)]
    public int OrderId { get; set; }

    [Key, Column(Order = 2)]
    public int OrderLineId { get; set; }

    public Order Order { get; set; }
}

如果需要,您可以制作OrderLineId自动生成的身份。重要的只是 FKOrder是 PK 的一部分。

例如这样的代码......

using (var ctx = new MyContext())
{
    var order = ctx.Orders.Include("OrderLines").Single(o => o.OrderId == 1);
    var orderLineToDelete = order.OrderLines
        .FirstOrDefault(ol => ol.OrderLineId == 5);
    if (orderLineToDelete != null)
        order.OrderLines.Remove(orderLineToDelete);

    ctx.SaveChanges();
}

...确实会从数据库中删除。orderLineToDelete

更多详细信息请参见识别和非识别关系的注意事项”部分。

于 2012-06-14T13:21:14.637 回答
2

正如您所发现的,如果您只是从集合中删除一个实体,则该实体会挂在附加到对象上下文中,并在您调用时给您一个错误SaveChanges();我已经使用域事件启用了一种通过存储库从对象上下文中删除实体的整洁方式。

我在对这个问题的回答中详细介绍了这种方法。

于 2012-06-14T13:09:33.667 回答