【问题标题】:Purpose of checking if the entity state is deleted before deleting删除前检查实体状态是否被删除的目的
【发布时间】:2015-04-04 16:30:38
【问题描述】:
    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 语句 的目的是什么? else statement的目的是什么? 正如我所测试的,DbSet.Remove(entity);dbEntityEntry.State = EntityState.Deleted 之间的区别在于,所有引用的行也在第一个代码中被删除,但在第二个代码中,EF 将抛出对该行具有 FK 的行的异常。

【问题讨论】:

    标签: c# .net entity-framework


    【解决方案1】:

    当您在 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 会在关系上设置级联删除,所以,当你调用Remove 方法时,它还会删除约束关系中的所有子对象(EF 将为父实体和相关实体生成删除语句)。如果依赖实体上的外键可以为空,Code First 不会对关系设置级联删除,当主体被删除时,外键将设置为null

    现在,当您使用将父实体的状态更改为Deleted (bEntityEntry.State = EntityState.Deleted) 的第二个变体时,EF 会将整个实体图附加到上下文中,并指定状态到父实体,并且对于子实体,三件事可以发生取决于您的关系类型:

      1234563调用SaveChanges 这个NULLchildEntity 值将被写入数据库(即删除两者之间的关系)。这发生在 SQL UPDATE 语句中。没有出现DELETE 语句。
    • 如果关系是必需(FK 不允许NULL 值)并且关系无法识别(这意味着外键不是孩子(复合)主键的一部分)您必须将孩子添加到另一个父母,或者您必须明确删除孩子(然后使用DeleteObject)。如果您不执行其中任何一项操作,则会违反引用约束,并且当您调用 SaveChanges 时,EF 将抛出异常 - 臭名昭著的 “关系无法更改,因为一个或多个外键属性是non-nullable" 异常或类似情况。

    • 如果关系是标识(它必然是必需,那么因为主键的任何部分都不能是NULL)EF 将标记childEntityDeleted 一样。如果您调用SaveChanges,一条SQL DELETE 语句将被发送到数据库。如果没有违反数据库中的其他引用约束,实体将被删除,否则抛出异常。

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

    所以,恕我直言,最好以这种方式创建一个通用的 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 泛型方法时会使用这两种变体之一,而不是同时使用两者。如果您使用DbSet.Remove 我看不到检查状态的意义,此方法将为您完成工作。如果你使用第二种变体,我会创建一个这样的方法:

    public virtual void Delete(T entity)
    {
        // An Added entity does not yet exist in the database. If it is then marked as deleted there is
        // nothing to delete because it was not yet inserted, so just make sure it doesn't get inserted.
       dbEntityEntry.State == EntityState.Added
                    ? EntityState.Detached
                    : EntityState.Deleted); 
    }
    

    【讨论】:

    • 感谢您提供信息,但说实话,我知道这一点。我的问题是,在删除之前检查实体状态是否被删除的目的是什么,如果其他条件为真,则采取不同的行动。看看我的问题中的方法。我无法理解那里的重点。我更新了我的问题以更好地解释我的问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-10
    • 1970-01-01
    • 2015-09-28
    • 2016-05-30
    • 1970-01-01
    相关资源
    最近更新 更多