Little times have I really feeled the need to use transactions in my code. They tend to complicate things quite a bit. Last week I realized I really needed one, but Entity Framework wasn't helping. By default, Entity Framework's SaveChanges will leave the DataContext in such state that it won't be possible to rollback the transaction. Fortunately, I found a work around after some minutes (hours) of learning (cursing).

Calling Entity Framework's SaveChanges Within a Transaction

A little background

So, last week I found myself doing operations complex enough for me to say for the first time "Geez, I sure need to use a transaction here" (I mean, it's not the first time I used one, just the first time that I positively, affirmatively thought I needed one).

Don't complicate your code!

The operation spanned more than three layers, deleting and saving files into file system (using this nice library for transactional file operations), saving, retrieving and updating files using entity framework, and then storing, updating and retrieving data to and from redis. It was a really big atomic operation.

Everything was going smooth, I have a clear overview of what a transaction is, how it works, and what does every isolation levels mean (it can be confusing if you're young enough, so I strongly recommend Martin Fowler's "Patterns of Enterprise Application Architecture"). Most importantly, I finished it (teehee!). It was deployment time.

The problem

The first problem I encountered myself with (and the one I will be teaching about in this post) is that entity framework won't (by default, that is) be able to rollback the database transaction once you call SaveChanges

It is not that it is not possible, though.

So, what is happening?

When you call SaveChanges, by default, Entity Framework will not only send to the DBMS you update and create statements, but also it will clear it's internal state. This allows it to get a new "fresh start", meaning that entities are not longer marked as updates, deleted, added, etc. A downside to this actions, is that entity framework will also purpousely forget what changes were made to begin with, so transaction rollback (at least, at the Database level) is not longer possible. Entity Framework can't rollback those changes because it already forgot them.

But behold, this is just a default behaviour in the Entity Framework in its efforts to have everything consistently.

The solution

As I metioned above, Entity Framework will forget about the changes once SaveChanges is called i order to have a fresh state ready to be modified. The solution here will be telling entity framework to NOT forget the changes.

This is easy to do. You only hace to call the ObjectContext's SaveChanges with the SaveOptions.None option, to explicitly tell it to not do anything to the state after saving changes. In Entity Framework 4.1, POCO style I do it like this:

using (var transactionScope = new TransactionScope(TransactionScopeOption.Required, 
                new TransactionOptions{ IsolationLevel = IsolationLevel.RepeatableRead }))
using (var ctx = new MyEntities())
{
    ...
    ... many transaction operations
    ((IObjectContextAdapter)ctx).ObjectContext.SaveChanges(SaveOptions.None);
    transactionScope.Complete();
}

Downsides

Of course, not everything is bells, party and whistles. The downside on this is that, for example if I added a new entity, after calling SaveChanges, it's state would still be "Added", so calling SaveChanges once again would cause it to be added again, causing an exception.

So, when your transaction is almost over, be sure to call AcceptAllChanges on your object context after completing your transaction scope. :)

I hope someone finds this useful.

Happy Coding!

Posted by: fabzter
Last revised: 07 Jan, 2014 04:10 PM History

Comments

Your Comments

Used for your gravatar. Not required. Will not be public.
Posting code? Indent it by four spaces to make it look nice. Learn more about Markdown.

Preview