0

I use EntityFramework 4.1.

What I want to achieve is, whenever SaveChanges method is called on entities that are FooEntity, first I want to UPDATE those entities and then DELETE after.

I try to override the default behaviour as below, but I can't achieve what I want: It updates on the Db. but not DELETES.

How can I achieve this?

public override int SaveChanges()
        {
            var entitiesMarkedAsDelete = ChangeTracker.Entries<FooEntity>()
                                    .Where(e => e.State==EntityState.Deleted);

            foreach (var e in entitiesMarkedAsDelete)
            {
                e.State = EntityState.Modified;
            }

            base.SaveChanges(); // To enforce an UPDATE first

            // Now, I try to re-mark them to DELETE
            foreach (var e in entitiesMarkedAsDelete)
            {
                e.State = EntityState.Deleted;
            }

            base.SaveChanges(); // And hope that they will be deleted

            // RESULT: 1st call of base.Savechanges() updates the entities
            //       but the 2nd call of base.Savechanges() does not make any changes on the UPDATED 
            //      entities -and they are NOT DELETED.

        }
pencilCake
  • 51,323
  • 85
  • 226
  • 363
  • I think 1st call of SaveChanges somehow refreshes the changeTracker and even though I change the EntityState to Deleted, it ignore this. But I don't know how to take care of this even if I am correct. – pencilCake Oct 11 '13 at 08:09
  • I'm sure you have good reason to do it...but an update before a delete to me seems pointless? – James Oct 11 '13 at 08:16
  • Well, it is somehow due the current design of Auditing the entity changes. And if I do not update first, I can't get the username who deleted the record. – pencilCake Oct 11 '13 at 08:21

1 Answers1

1

I think you are being fooled by the lazy nature of LINQ here.

The sequence you create here:

var entitiesMarkedAsDelete = ChangeTracker.Entries<FooEntity>()
                             .Where(e => e.State==EntityState.Deleted);

.. is being evaluated each time it is being iterated, so the second time you foreach over it, you don't actually get any results back, since there are no entities in the deleted state anymore.

So to fix this, you simply have to pipe the sequence into fx. a list, by calling .ToList on it:

var entitiesMarkedAsDelete = ChangeTracker.Entries<FooEntity>()
                             .Where(e => e.State==EntityState.Deleted).ToList();

That way both foreach statements will iterate over the same entities, and they should be deleted properly.

Peter Hansen
  • 8,807
  • 1
  • 36
  • 44
  • Peter this helped what I wanted to achieve. In the 1st SaveChanges, it commits and in the 2nd one it deletes. But I think they are not done within a transaction at all so if the DELETE fails, the UPDATE won't be rolled back. Right? – pencilCake Oct 11 '13 at 08:51
  • 1
    Yeah, the way it works now the two calls are 2 different transactions, so it wont roll the updates back. But you should be able to combine them in one transaction. [Here is some info](http://stackoverflow.com/questions/12809958/ef-how-do-i-call-savechanges-twice-inside-a-transaction) – Peter Hansen Oct 11 '13 at 09:01
  • Last thing: Do you think it would make sense to include also the .Detached states? – pencilCake Oct 11 '13 at 09:49
  • Hmm, no can't really see why that would make any sense :) – Peter Hansen Oct 11 '13 at 09:57