4
    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);
        }
    }

Of course this method is identical to this:

    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; 
        }
    }

I have seen above code in this article. IMHO some parts of this code is redundant. What is the purpose of if statement? And what is the purpose of else statement? As I have tested, difference between DbSet.Remove(entity); and dbEntityEntry.State = EntityState.Deleted is that, all referenced rows are also deleted in first code, but in second one EF will throw exception about rows which has FK to this row.

Farhad Jabiyev
  • 26,014
  • 8
  • 72
  • 98

1 Answers1

2

When you call the DbSet.Remove(entity) in EF 6.X, you are really using the ObjectContext.DeleteObject(entity). This is the code that is called in the InternalSet<TEntity> class:

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);
}

If the foreign key on the dependent entity is not nullable, by default, when you configure your relationship, Code First sets cascade delete on the relationship,so, when you call the Remove method, it also deletes all the child objects in the constrained relationship (EF will generate the delete statements for the parent entity and the related entities). If a foreign key on the dependent entity is nullable, Code First does not set cascade delete on the relationship, and when the principal is deleted the foreign key will be set to null.

Now, when you use the second variant changing the state of a parent entity as Deleted (bEntityEntry.State = EntityState.Deleted), EF attaches a whole entity graph to the context with specified state to parent entity and with the children three things can happen depending on the type of relationship you have:

  • If the relationship is optional, i.e. the foreign key that refers from the child to the parent in the database allows NULL values, this foreign will be set to null and if you call SaveChanges this NULL value for the childEntity will be written to the database (i.e. the relationship between the two is removed). This happens with a SQL UPDATE statement. No DELETE statement occurs.

  • If the relationship is required (the FK doesn't allow NULL values) and the relationship is not identifying (which means that the foreign key is not part of the child's (composite) primary key) you have to either add the child to another parent or you have to explicitly delete the child (with DeleteObject then). If you don't do any of these a referential constraint is violated and EF will throw an exception when you call SaveChanges - the infamous "The relationship could not be changed because one or more of the foreign-key properties is non-nullable" exception or similar.

  • If the relationship is identifying (it's necessarily required then because any part of the primary key cannot be NULL) EF will mark the childEntity as Deleted as well. If you call SaveChanges a SQL DELETE statement will be sent to the database. If no other referential constraints in the database are violated the entity will be deleted, otherwise an exception is thrown.

Fo more info, you can check this post and this too.

So, IMHO it's better create a generic Delete method this way:

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); 
}

Update

Me neither see the point of the author in use both variants.Usually when you create a Remove generic method is used one of this two variants, not both at the same time. If you use the DbSet.Remove I don't see the point in check the state, this method will do the job for you. If you use the second variant, I would create a method like this:

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); 
}
Community
  • 1
  • 1
ocuenca
  • 38,548
  • 11
  • 89
  • 102
  • Thanks for information, but to tell the truth I was aware of this points. My question is, what is the Purpose of checking if the entity state is deleted before deleting and acting differentle if else condition is true. Look at the method in my question. I can't get the point there. I have updated my questtion to explain my problem better. – Farhad Jabiyev Apr 05 '15 at 05:12