Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Difference between DbSet.Remove and DbContext.Entry(entity).State = EntityState.Deleted

Consider the following entity model:

public class Agreement : Entity
{
    public int AgreementId { get; set; }
    public virtual ICollection<Participant> Participants { get; set; }
}

public class Establishment : Entity
{
    public int EstablishmentId { get; set; }
}

public class Participant : Entity
{
    public int AgreementId { get; set; }
    public virtual Agreement Agreement { get; set; }

    public int EstablishmentId { get; set; }
    public virtual Establishment { get; set; }

    public bool IsOwner { get; set; }
}

The reason there is not a direct ICollection<Establishment> on the Agreement entity is because of the IsOwner property, which further defines this many-to-many relationship.

The relationship is mapped like so:

internal ParticipantOrm()
{
    ToTable(typeof(Participant).Name);

    HasKey(k => new { k.AgreementId, k.EstablishmentId });

    HasRequired(d => d.Agreement)
        .WithMany(p => p.Participants)
        .HasForeignKey(d => d.AgreementId)
        .WillCascadeOnDelete(true);

    HasRequired(d => d.Establishment)
        .WithMany()
        .HasForeignKey(d => d.EstablishmentId)
        .WillCascadeOnDelete(true);
}

The relationship is uni-directional, meaning you can only get to the Participants from within an Agreement -- you cannot access the agreements from the Establishment.

Now, since the Agreement entity's primary key is part of the gerund Participant's primary key, and since cascade delete is specified, I would expect that putting the Agreement into the Deleted state would also cause each entity in its Participants collection to also be put into the deleted state.

This appears to work when calling Remove on an Agreement DbSet:

// this works
dbContext.Agreements.Remove(agreement);
dbContext.SaveChanges();

However, it does not appear to work when simply setting the agreement entry's state to deleted:

// this causes an exception when Participants is not empty
dbContext.Entry(agreement).State = EntityState.Deleted;
dbContext.SaveChanges();

Is there a way I can define the relationship so that simply putting the Agreement entity into the deleted state will also cause the corresponding collection item entities to be put into the deleted state?

Update

I have been reading about this, and found out that eager loading the Participants collection does not help:

// eager loading does not help, this still breaks
var agreement = dbContext.Set<Agreement>()
    .Include(a => a.Participants)
    .FirstOrDefault();
dbContext.Entry(agreement).State = EntityState.Deleted;
dbContext.SaveChanges();
like image 681
danludwig Avatar asked Jul 20 '12 17:07

danludwig


People also ask

What does EntityState Modified do?

When you change the state to Modified all the properties of the entity will be marked as modified and all the property values will be sent to the database when SaveChanges is called.

What is EntityState?

EF API maintains the state of each entity during its lifetime. Each entity has a state based on the operation performed on it via the context class. The entity state represented by an enum System.

How does DbContext change state of entity?

This can be achieved in several ways: setting the EntityState for the entity explicitly; using the DbContext. Update method (which is new in EF Core); using the DbContext. Attach method and then "walking the object graph" to set the state of individual properties within the graph explicitly.


1 Answers

Ended up solving this by calling the following:

dbContext.Set<Agreement>().Remove(agreement);

I wanted to get rid of the Agreements property on the DbContext, which is why I was trying to do it with Entry(agreement).State = EntityState.Deleted.

like image 160
danludwig Avatar answered Oct 22 '22 10:10

danludwig