1

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

Rick B
  • 1,156
  • 10
  • 11

1 Answers1

2

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.

Davin Tryon
  • 66,517
  • 15
  • 143
  • 132
  • I agree, it does make sense. `SaveChanges` opens and closes the connection itself. I was just hoping there was a better answer :) Restructuring is possible, but would result in duplicated code. I have a DeleteCart method that deletes the cart. This can be called by consumers of my service directly, but also internally at other times (there are many ways a cart can be deleted). – Rick B Nov 27 '12 at 20:32
  • It might not mean duplicate code. For example, if you are building a web application, there are patterns that will open up a dbcontext at the beginning of the request and then `SaveChanges()` at the end of the request. Therefore, anything that happens inside the request is committed. This is leaning towards more true persistence ignorance. – Davin Tryon Nov 27 '12 at 20:34
  • This is a good reference question: http://stackoverflow.com/questions/10585478/one-dbcontext-per-web-request-why – Davin Tryon Nov 27 '12 at 20:36
  • Thanks for the reference. I can see how what you said could be used very nicely in the command / handler pattern. – Rick B Nov 27 '12 at 21:26
  • Marked as answer because this led me to my answer which is to handle the transactions at a higher level. – Rick B Nov 30 '12 at 19:56