Most of the time the Entity Framework (EF) can manage transactions for you.

Every time you Add an Entity, Delete an Entity, Change an Entity, Create a Relationship or Delete a Relationship in your .NET code, these changes are remembered by the EF, and when you call SaveChanges()these are converted to appropriate native SQL commands and executed in the database in a transaction.

Sometimes however you want to use your own transaction. Situations where this is useful include:

  • Working with an object context and attempting to put a message in a message queue within the same transaction.
  • Working with two object contexts simultaneously.
  • Etc etc etc… You get the idea.

In these situations you want the EF to use an ambient transaction (TransactionScope) but more importantly if something goes wrong outside of the EF, you want to be able to recover.

If you call SaveChanges() or SaveChanges(true),the EF simply assumes that if its work completes okay, everything is okay, so it will discard the changes it has been tracking, and wait for new changes.

Unfortunately though if something goes wrong somewhere else in the transaction, because the EF discarded the changes it was tracking, we can’t recover.

This is where SaveChanges(false) and AcceptAllChanges() come in.

SaveChanges(false) tells the EF to execute the necessary database commands, but hold on to the changes, so they can be replayed if necessary.

Now if the broader transaction fails you can retry the EF specific bits, with another call to SaveChanges(false). Alternatively you can walk through the state-manager to log what failed.

Once the broader transaction succeeds, you simply call AcceptAllChanges() manually, and the changes that were being tracked are discarded.

Typically pseudo-code for this is something like this…

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


    //Save Changes but don’t discard yet

    //Save Changes but don’t discard yet


    //if we get here things are looking good.

    //If we get here it is save to accept all changes.



If you fall out of the using block because of an exception you can now potentially retry.

Make sense?

Comments (6)

  1. David says:


    Thanks for you comprehensive article on SaveChanges(False) article. I would like to know why we can’t call to both context1.AceptAllChanges() and context2.AceptAllChanges() directly and then followed by the scope.Complete(). Since we are already in the transactionscope, then the transactionscope should do all the transaction for us instead of having us to take care of it.




  2. Alex D James says:


    I hear ya it is a little confusing… Here is my probably lame attempt at explaining that:

    Well while the updates to the database are happening in a transaction, the changes to the ObjectStateEntries in the ObjectContext are not transactional…

    i.e. if they go from modified to unchanged – which is what happens when you call SaveChanges(true)) – you can’t go back and you no longer no what entities and what properties are out of sync with the database.


  3. TheEagle says:

    Hi Alex,

    I’m trying to do the same idea but in my case the 2 context instances represent the same context class:

    using(TransactionScope tscope=new TransactionScope())




    if (vEntity1.EntityState == EntityState.Detached)






      if (vEntity2.EntityState == EntityState.Detached)






    success = true;  


    catch(Exception ex)


     success = false;

     ltlStatus.Text = ex.Message;








    However I got an error after executing the line:


    [System.InvalidOperationException] = {"The relationship cannot be defined because the EntitySet name ‘contextClass.Entity3Set’ is not valid for the role ‘Entity1Table’ in association set name ‘contextClass.FK_Entity2Table_Entity1Table’."}

    About Entity relations:


    The code has nothing to do with Entity3 so what could cause this error?

  4. TheEagle says:

    I wonder in your article:is it possible to use two context instances for the same Object Context Class?

  5. just me says:

    the problem is that it doesnt work… it throws an error on context2.SaveChanges(false). the error message says: underlying connection is closed