26

We have a scenario to save two entities from two contexts in single transaction.

Step 1 - SetTransaction(firstContext, true);

Step 2 - Save first entity using firstContext.

Step 3 - SetTransaction(secondContext, false);

Step 4 - Save second entity using secondContext

Step 5 - finally commit the transaction.

void function SetTransaction(context, startNewTransaction)
{    
   var currentContext = firstContext;

   if (startNewTransaction)
   {
      var connection = currentContext.GetConnection();
      connection.Open();
      this.dbTransaction = connection.BeginTransaction();
   }

   if (this.dbTransaction != null)
   {
       currentContext.UseTransaction(dbTransaction);
   }
}

While executing Step 3, currentContext.UseTransaction(dbTransaction); line throws the exception as "The transaction passed in is not associated with the current connection. Only transactions associated with the current connection may be used"

Please suggest how to resolve.

Venkat.

2 Answers 2

43

Use the TransactionScope. EF will automatically enlist in a running transaction-scope.

It will require that your connectionstrings are identical.

using (var scope = new TransactionScope()) {
    // Save entity in context A
    using (var contextA = new ContextA()) {
        contextA.Save(...);
        contextA.SaveChanges;
    }
    // Save entity in context B
    using (var contextB = new ContextB()) {
        contextB.Save(...);
        contextB.SaveChanges;
    }
    // Commit tx-scope
    scope.Complete();
}
Sign up to request clarification or add additional context in comments.

5 Comments

identical just in respect to the way transactions should be managed.
I suppose you meant scope.Complete() instead of scope.Commit(), according to msdn documentation. Commit method doesn't exist.
@marco or maarten Could you elaborate on what way the connection strings need to be identical?
It must be the same, otherwise you could (and most likely will) be promoted to DTC.
Will this still work if the contexts are created in different methods (and therefore, different scopes)?
1

Using TransactionScope is not recommanded from EF 6 onward. So use:

using (var conn = new SqlConnection("..."))
        {
           conn.Open();

           using (var sqlTxn = conn.BeginTransaction(System.Data.IsolationLevel.ReadCommitted))
           {
               try
               {
                   using (var contextA = new ContextA(conn, contextOwnsConnection: false))
                    {
                        contextA.Database.UseTransaction(sqlTxn);
                        contextA.Save(...);
                        contextA.SaveChanges();
                    }

                    using (var contextB = new ContextB(conn, contextOwnsConnection: false))
                    {
                        contextB.Database.UseTransaction(sqlTxn);
                        contextB.Save(...);
                        contextB.SaveChanges();
                    }

                    sqlTxn.Commit();
                }
                catch (Exception)
                {
                    sqlTxn.Rollback();
                }
            }
        }
    }

Edit: If your dbContext does not have required constructor, You can add another constructor as follow:

public ContextA(): base("name=ConnectionString")
{

}

public ContextA(DbConnection connection) : base(connection, false)
{

}

3 Comments

This may be well and good, BUT in my Context i could not find a constructor with the overload ContextA(conn, contextOwnsConnection: false)); the only constructor I have is parameterless. I am using EF6 !
thanks BUT not as simple as you suggested. RUNTIME ERROR IS (too long... part 1) Additional information: The context is being used in Code First mode with code that was generated from an EDMX file for either Database First or Model First development. This will not work correctly. To fix this problem do not remove the line of code that throws this exception. If you wish to use Database First or Model First, then make sure that the Entity Framework connection string is included in the app.config or web.config of the start-up project. If you are creating your own DbConnection, then
ERROR part 2: If you are creating your own DbConnection, then make sure that it is an EntityConnection and not some other type of DbConnection, and that you pass it to one of the base DbContext constructors that take a DbConnection. To learn more about Code First, Database First, and Model First see the Entity Framework documentation here: go.microsoft.com/fwlink/?LinkId=394715

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.