views:

8750

answers:

1

Hi there,

I have been investigating transactions and it appears that they take call of them selves in EF as long as i pass false to savechanges..

SaveChanges(false)

and if all goes well then AcceptAllChanges()

Question is what is something goes bad, don't have to rollback? or as soon as the my method goes out of scope its ended?

What happens to any indentiy columns that were assigned half way through the transaction.. i presume if somebody else added a record after mine before mine went bad then this means there will be a missing Identity value.

Is there any reason to use standard "transactionScope" in code?

ideas? - thanks

+19  A: 

With the Entity Framework most of the time SaveChanges() is sufficient. This creates a transaction, or enlists in any ambient transaction, and does all the necessary work in that transaction.

Sometimes though the SaveChanges(false) + AcceptAllChanges() pairing is useful.

The most useful place for this is in situations where you want to do a distributed transaction across two different Contexts.

I.e. something like this (bad):

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save and discard changes
    context1.SaveChanges();

    //Save and discard changes
    context2.SaveChanges();

    //if we get here things are looking good.
    scope.Complete();
}

If context1.SaveChanges() succeeds and but context2.SaveChanges() fails the whole distributed transaction is aborted. But unfortunately the Entity Framework has already discarded the changes on context1, so you can't replay or effectively log the failure.

But if you change your code to look like this:

using (TransactionScope scope = new TransactionScope())
{
    //Do something with context1
    //Do something with context2

    //Save Changes but don't discard yet
    context1.SaveChanges(false);

    //Save Changes but don't discard yet
    context2.SaveChanges(false);

    //if we get here things are looking good.
    scope.Complete();
    context1.AcceptAllChanges();
    context2.AcceptAllChanges();

}

While the call to SaveChanges(false) sends the necessary commands to the database, the context itself is not changed, so you can do it again if necessary, or you can interrogate the ObjectStateManager if you want.

This means if the transaction actually aborts you can compensate, by either re-trying or logging state of each contexts ObjectStateManager somewhere.

See this blog post for more

Hope this helps

Alex

Alex James
Thats great, thanks... So if something fails don't i have to rollback?? SaveChanges, marks it for being saved, but doesn't actually commit until i do acceptallchanges.. but if something goes wrong.. i will need to rollback won't i so that my object returns to its correct state?
mark smith
@Mark: if by "roll-back" you mean, revert your objects to the state that they are in in the database, then no, you wouldn't want to do that because you'd lose all the user's changes to the objects. `SaveChanges(false)` does the actual updating to the database, while `AcceptAllChanges()` tells EF, "Okay, you can forget which things need to be saved, because they've been sucessfully saved." If `SaveChanges(false)` fails, `AcceptAllChanges()` will never be called and EF will still consider your object as having properties that were changed and need to be saved back to the database.
BlueRaja - Danny Pflughoeft