在删除之前检查实体状态是否被删除的目的

时间:2015-04-04 16:30:38

标签: c# .net entity-framework

    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到此行的行的异常。

1 个答案:

答案 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

现在,当您使用第二个变体将父实体的状态更改为nullDeleted)时,EF会将整个实体图附加到具有指定状态到父实体的上下文中,并且孩子们可能会发生三件事情,具体取决于你们的关系类型:

  • 如果关系是可选,即从子级引用到数据库中的父级的外键允许bEntityEntry.State = EntityState.Deleted值,则此外部将设置为null并且如果您调用NULLSaveChanges的{​​{1}}值将写入数据库(即删除两者之间的关系)。这发生在SQL NULL语句中。没有childEntity声明。

  • 如果关系必需(FK不允许UPDATE值)且关系无法识别(这意味着如果外键不是子代(复合)主键的一部分,则必须将子项添加到另一个父项,或者必须显式删除子项(然后使用DELETE)。如果您没有执行任何这些操作,则会违反参考约束,当您致电NULL时,EF会抛出异常 - 臭名昭着的&#34;由于一个或多个关系无法更改外键属性的属性是不可为空的&#34; 异常或类似。

  • 如果关系是识别(它必须必需,那么因为主键的任何部分都不能是DeleteObject)EF也会将SaveChanges标记为NULL。如果您调用childEntity,则会将SQL Deleted语句发送到数据库。如果数据库中没有违反其他参照约束,则实体将被删除,否则将引发异常。

了解更多信息,您也可以查看此postthis

所以,恕我直言,最好以这种方式创建一个通用的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