Search code examples
c#.netentity-frameworktransactionscope

EF5 Code First TransactionScope without escalating to MSDTC


I'm using EF5 code first, and I need to wrap multiple calls to SaveChanges() in the same transaction. I'm relatively new to using transactionScope, and can't seem to tweak the options on it to accomplish what I'm looking to do.

The following code resides in a service class that gets injected with an instance of my DbContext class. I do not create additional instances of DbContext anywhere in this class.

At first I tried the following. The code is nice and clean, but it attempts to escalate to to DTC during the SaveChanges method in the GenerateInvoice function.

using (var scope = new TransactionScope())
{
    // This function does some work and calls SaveChanges
    Guid invoiceId = GenerateInvoice(cart);  

    // This function also does some querying and calls SaveChanges
    DeleteCart();

    // Transaction should only commit if both functions above were successful
    scope.Complete();

}

I did some research and changed the code to the following which does not escalate to DTC, but I'm not sure if it's the best approach.

IObjectContextAdapter adapter = (IObjectContextAdapter)_context;

adapter.ObjectContext.Connection.Open();

using (var scope = new TransactionScope())
{

    // This function does some work and calls SaveChanges
    Guid invoiceId = GenerateInvoice(cart);  

    // This function also does some querying and calls SaveChanges
    DeleteCart();

    // Transaction should only commit if both functions above were successful
    scope.Complete();

    adapter.ObjectContext.Connection.Close();

}

Is there a simpler way to get transaction support with EF5 code first across multiple SaveChanges without escalating to DTC?

Thanks


Solution

  • Not that I have found. And it makes sense as well. By calling SaveChanges() more than once, you are potentially opening up multiple connections to the database. The only way to coordinate the transaction across these connections is with a distributed transaction (hence MSDTC).

    The best solution I could come up with was to only call SaveChanges() once. This might mean restructuring some code, but it is best if all the entities that are needed to be transactional all commit at the same time.

    Only calling SaveChanges() once also allows the EF change tracker to use its cache and save a lot of communication between the app and the db.