实体框架DbContext .Remove(obj)vs .Entry(obj).State = EntityState.Deleted

Moh*_*eza 7 c# entity-framework

我先使用EF代码.一个简单的模型:

  item { public int Id {set; get;},... ,ICollection<ItemImages> {set; get;} }

    itemImages { 
         public int Id {set; get; },
         public int ItemId {set; get; }
          , ... ,
         public Item Item  {set; get; }
      }

ItemConfig:EntityTypeConfiguration<Item>
{
 //some config statement;
 //...
// mark child delete when parent delete: waterfall delete.
 HasRequired(rs => rs.ItemCat).WithMany(rs => rs.Items).HasForeignKey(rs => rs.ItemCatId).WillCascadeOnDelete(true);
}
Run Code Online (Sandbox Code Playgroud)

当通过Remove()删除实体时,它会很好地删除项目和相关子项(项目图像记录).

_db.Item.Remove(DeleteThisObj);
_db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

但标记为删除时:

_db.Entry(DeleteThisObj).State = EntityState.Deleted;
_db.SaveChanges();
Run Code Online (Sandbox Code Playgroud)

得到错误:

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

Mat*_*ius 14

如果你真的想使用Deleted,你必须使你的外键可以为空,但是你最终会得到孤立的记录(这是你不应该首先做的那个主要原因之一).所以只需使用Remove()

ObjectContext.DeleteObject(entity)在上下文中将实体标记为已删除.(之后删除了EntityState.)如果之后调用SaveChanges,EF会向数据库发送SQL DELETE语句.如果违反了数据库中的引用约束,则将删除该实体,否则将引发异常.

EntityCollection.Remove(childEntity)将parent和childEntity之间的关系标记为已删除.如果从数据库中删除了childEntity本身,并且在调用SaveChanges时究竟发生了什么,取决于两者之间的关系类型:

如果关系是可选的,即从子级引用到数据库中的父级的外键允许NULL值,则此外部将设置为null,如果调用SaveChanges,则将此childEntity的NULL值写入数据库(即两者之间的关系被删除).使用SQL UPDATE语句会发生这种情况.没有DELETE语句.

如果需要关系(FK不允许NULL值)且关系未识别(这意味着外键不是子(的)复合主键的一部分),则必须将子项添加到另一个父项或者您必须显式删除子项(然后使用DeleteObject).如果您不执行任何这些参数约束,则在调用SaveChanges时EF将抛出异常 - 臭名昭着的"由于一个或多个外键属性不可为空而无法更改关系"异常或类似的.

如果关系正在识别(因为主键的任何部分都不能为NULL,则必然需要),EF也会将childEntity标记为已删除.如果调用SaveChanges,则会将SQL DELETE语句发送到数据库.如果未违反数据库中的其他引用约束,则将删除该实体,否则将引发异常.

值得注意的是,设置.State = EntityState.Deleted 不会触发自动检测到的更改.