更改父属性并删除子项时,实体框架引发异常

时间:2018-12-28 15:23:02

标签: c# entity-framework-6

    // The code below generates exception:
    try
    {
        using (var x = new CourierContext())
        {
            Shipment s = x.Shipments.Single(a => a.ShipmentID == 10);
            s.ShipmentItems.Remove(s.ShipmentItems.Single(a => a.ShipmentItemID == 4));
            x.SaveChanges();
         }
     }
     catch (Exception e)
     { MessageBox.Show(e.Message); }

     // The code below works fine:
     try
     {
         using (var x = new CourierContext())
         {
             x.ShipmentItems.Remove(x.ShipmentItems.Single(a => a.ShipmentItemID == 4));
             x.SaveChanges();
         }
     }
     catch (Exception e)
     { MessageBox.Show(e.Message); }

当我从父级的子级集合中删除一个子级(使用Remove()方法)时,在SaveChanges()上收到以下异常。当我从DbSet的children集合中删除一个child时,SaveChanges()可以正常工作。就我而言,托运(父代)可以有0个或多个子代(PackingItem)。在子表中,ItemID为PK [自动编号],父项的FK为非空。我的问题是:为什么尝试从父级的子级集合中删除失败?

我在VS2017中将EF 6.2与基于C#表单的应用程序一起使用。首先使用数据库自​​动创建域类和关系。 发货表: ShipmentID [int,PK,身份]

PackingItem表: PackingItemID [int,PK,Identity] ShipmentID [int,FK,Not Null]

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

我已经阅读了有关此例外的信息,有人评论了EF如何对待它们的组合/聚集和FK。

1 个答案:

答案 0 :(得分:0)

发生了什么事?

异常消息很难听,但它确实包含了您需要的所有信息。

  

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

让我们分解一下:

  

关系无法更改...

EF尝试修改两个实体之间的RELATIONSHIP。这些关系作为PRIMARY / FOREIGN密钥对存储在数据库中。与下一部分匹配。

  

...,因为一个或多个外键属性不可为空。对关系进行更改时,相关的外键属性将设置为空值。

好,因此EF尝试将FK设置为​​null,但是有数据库约束阻止了它。

  

如果外键不支持空值,则必须定义新的关系,必须为外键属性分配另一个非空值,或者必须删除不相关的对象。 (强调我的)

这是最终的解决方案,因此可以长期有效。


为什么会这样?

假设:您已将ShipmentItem定义为具有必需的Shipment

写作时

s.ShipmentItems.Remove(s.ShipmentItems.Single(a => a.ShipmentItemID == 4));

您正在告诉EF从父集合中删除一个项目。这只是告诉EF删除两个对象之间的关系

写作时

x.ShipmentItems.Remove(x.ShipmentItems.Single(a => a.ShipmentItemID == 4));

您正在告诉EF从数据库中删除一项。这明确地告诉EF删除该项目(并通过扩展将其从其所属的所有集合中删除)。


为什么这是正确的设计?

假设您有不同的关系,即某个班级由若干位老师教,但每位老师只教一个班。

class Class {
    Teachers: Collection<Teacher>;
}

class Teacher {
     Class: Class;
}

如果我们从班级中删除教师,则我们不想删除该教师。我们只是想结束这段关系。

c.Teachers.Remove(teacher);

EF无法知道此删除与您的ShipmentItems删除有何不同。因此,为避免数据丢失(和坏东西),EF假定除非明确告知要删除它们,否则数据库中的所有项目都应保留在该位置。