Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to rollback Entity Delete without losing Navigational Properties

We are using Entity Framework Code First, and I am running into issues trying to rollback entity changes for Insert, Update, and Delete when we don't want to SaveChanges().

Specifically, I have a datagridview which I am using as a TableEditor, to make changes to some auxiliary tables. The datagridview is bound to DbSet<TEntity>.

My rollback for Update seems to work fine, I just set the currentValues to back to their OriginalValues, and change state to unchanged.

When a record is inserted to the gridview (but no changes saved), it never shows up in the entity class, and I never see it again... So I guess it doesn't make it to the dbSet, and no rollback is needed for this?

But my main problem lies with Delete:

From what I understand, when a record is "deleted" (eg.tableData.Remove(currentItem);), it is simply marked for deletion until SaveChanges is called. So if I change the State from deleted back to unchanged, that should handle the rollback, right?

Well, the record does show back up again, BUT the navigational properties of the record are gone! (ie. the columns containing foreign keys and required relationships to other entities). Why is this??!

Here is what I have so far:

    public void RollbackChanges(DbEntityEntry entry)
    {
        if (entry.State == EntityState.Modified)
        {
            foreach (var propertyName in entry.OriginalValues.PropertyNames)
            {
                entry.CurrentValues[propertyName] = entry.OriginalValues[propertyName];
            }
            entry.State = EntityState.Unchanged;
        }
        else if (entry.State == EntityState.Deleted)
        {
            entry.State = EntityState.Unchanged;
        }
        else if ((entry.State == EntityState.Added) || (entry.State == EntityState.Detached))
        {
            MessageBox.Show("I don't think this ever happens?");
        }
    }

Example usage:

    foreach (var entity in db.CertificationDecisions)
                {
                    DbEntityEntry entry = db.Entry(entity );
                    if (entry.State != EntityState.Unchanged)
                    {
                        RollbackChanges(entry);
                    }
                }

Any ideas why the navigational properties would disappear from the record? (Or what I can do to get them back?)


EDIT: @Chris regarding using Refresh:

I am using a DbContext, so I replaced my rollback methods with this line:

((IObjectContextAdapter)db).ObjectContext.Refresh(RefreshMode.StoreWins, db.CertificationDecisions);

However, this does not seem to reload the context, as the record is still missing... Am I using Refresh wrong?

This sounds like a possible solution to my problem, but I am still wondering why the navigation properties would be removed?

like image 637
sǝɯɐſ Avatar asked Dec 19 '12 17:12

sǝɯɐſ


1 Answers

Any ideas why the navigational properties would disappear from the record?

The navigation properties are most likely removed due to a foreign-key relationship. Lets take the following classes:

public class Dog
{
  public guid ID { get; set; }
  public string Title { get; set; }
}

public class Person
{
  public guid ID { get; set; }

  public ICollection<Dog> FavoriteDogs { get; set; }
}

In the database, the collection of FavoriteDogs is a table that has a reference to both a Person and a Dog, but is hidden automatically by the navigation properties. When the Parent state is set to Deleted, EF automatically marks the reference entities as Deleted. Entity Framework will not set these reference entities to Unchanged if the Parent entity is set to Unchanged because there is no referential integrity required to do so (only for delete operations).

Or what I can do to get them back?

There is currently no automated process within EF that will do this. I don't believe there is even a manual way to change reference entities. And the new Entity Framework 5 does not expose any way to view or change the state of DbRefrenceEntry.

Maybe try DbReferenceEntry.Reload.

like image 178
Erik Philips Avatar answered Oct 29 '22 17:10

Erik Philips