public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State != EntityState.Deleted)
{
dbEntityEntry.State = EntityState.Deleted;
}
else
{
DbSet.Attach(entity);
DbSet.Remove(entity);
}
}
当然这种方法与此相同:
public virtual void Delete(T entity)
{
DbEntityEntry dbEntityEntry = DbContext.Entry(entity);
if (dbEntityEntry.State == EntityState.Deleted)
{
DbSet.Attach(entity);
DbSet.Remove(entity); // Why to remove here??
}
else
{
// And why to set as Deleted here??
dbEntityEntry.State = EntityState.Deleted;
}
}
我在this文章中看到了上述代码。恕我直言这段代码的一些部分是多余的。 if statement 的目的是什么? 其他声明的目的是什么?
正如我测试的那样,DbSet.Remove(entity);
和dbEntityEntry.State = EntityState.Deleted
之间的区别在于,所有引用的行也会在第一个代码中被删除,但在第二个代码中,EF会抛出有关FK到此行的行的异常。
答案 0 :(得分:2)
当您在EF 6.X中呼叫DbSet.Remove(entity)
时,您实际上正在使用ObjectContext.DeleteObject(entity)。这是在InternalSet<TEntity>
类中调用的代码:
public virtual void Remove(object entity)
{
DebugCheck.NotNull(entity);
if (!(entity is TEntity))
{
throw Error.DbSet_BadTypeForAddAttachRemove("Remove", entity.GetType().Name, typeof(TEntity).Name);
}
InternalContext.DetectChanges();
InternalContext.ObjectContext.DeleteObject(entity);
}
如果依赖实体上的外键不可为空,默认情况下,在配置关系时,Code First会在关系上设置级联删除,因此,当您调用{{1}时方法,它还删除约束关系中的所有子对象( EF将为父实体和相关实体生成删除语句)。如果依赖实体上的外键可以为空,则Code First不会在关系上设置级联删除,并且当删除主体时,外键将设置为Remove
。
现在,当您使用第二个变体将父实体的状态更改为null
(Deleted
)时,EF会将整个实体图附加到具有指定状态到父实体的上下文中,并且孩子们可能会发生三件事情,具体取决于你们的关系类型:
如果关系是可选,即从子级引用到数据库中的父级的外键允许bEntityEntry.State = EntityState.Deleted
值,则此外部将设置为null并且如果您调用NULL
,SaveChanges
的{{1}}值将写入数据库(即删除两者之间的关系)。这发生在SQL NULL
语句中。没有childEntity
声明。
如果关系必需(FK不允许UPDATE
值)且关系无法识别(这意味着如果外键不是子代(复合)主键的一部分,则必须将子项添加到另一个父项,或者必须显式删除子项(然后使用DELETE
)。如果您没有执行任何这些操作,则会违反参考约束,当您致电NULL
时,EF会抛出异常 - 臭名昭着的&#34;由于一个或多个关系无法更改外键属性的属性是不可为空的&#34; 异常或类似。
如果关系是识别(它必须必需,那么因为主键的任何部分都不能是DeleteObject
)EF也会将SaveChanges
标记为NULL
。如果您调用childEntity
,则会将SQL Deleted
语句发送到数据库。如果数据库中没有违反其他参照约束,则实体将被删除,否则将引发异常。
所以,恕我直言,最好以这种方式创建一个通用的SaveChanges
方法:
DELETE
我既没有看到作者使用两种变体的观点。通常在创建Delete
泛型方法时,使用这两种变体中的一种,而不是同时使用这两种变体。如果你使用public virtual void Delete(T entity)
{
//Attach the entity to your context
DbSet.Attach(entity);
// Remove the current entity and the related entities in case of your relationship was configured with cascade delete
DbSet.Remove(entity);
}
我没有看到检查状态的点,这个方法将为你完成这项工作。如果您使用第二个变体,我会创建一个这样的方法:
Remove