// 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。
答案 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假定除非明确告知要删除它们,否则数据库中的所有项目都应保留在该位置。