Simpler Transactions

0 comments suggest edit

The .NET Framework provides support for managing transactions from code via the System.Transactions infrastructure. Performing database operations in a transaction is as easy as writing a using block with the TransactionScope class.

using(TransactionScope transaction = new TransactionScope()) 
{
  DoSomeWork();
  SaveWorkToDatabase();

  transaction.Complete();
}

At the end of the using block, Dispose is called on the transaction scope. If the transaction has not been completed (in other words, transaction.Complete was not called), then the transaction is rolled back. Otherwise it is committed to the underlying data store.

The typical reason a transaction might not be completed is that an exception is thrown within the using block and thus the Complete method is not called.

This pattern is simple, but I was looking at it the other day with a co-worker wondering if we could make it even simpler. After all, if the only reason a transaction fails is because an exception is thrown, why must the developer remember to complete the transaction? Can’t we do that for them?

My idea was to write a method that accepts an Action which contains the code you wish to run within the transaction. I’m not sure if people would consider this simpler, so you tell me. Here’s the usage pattern.

public void SomeMethod()
{
  Transaction.Do(() => {
    DoSomeWork();
    SaveWorkToDatabase();
  });
}

Yay! I saved one whole line of code! :P

Kidding aside, we don’t save much in code reduction, but I think it makes the concept slightly simpler. I figured someone has already done this as it’s really not rocket science, but I didn’t see anything after a quick search. Here’s the code.

public static class Transaction 
{
  public static void Do(Action action) 
  {
    using (TransactionScope transaction = new TransactionScope())
   {
      action();
      transaction.Complete();
    }
  }
}

So you tell me, does this seem useful at all?

By the way, there are several overloads to the TransactionScope constructor. I would imagine that if you used this pattern in a real application, you’d want to provide corresponding overloads to the Transaction.Do method.

UPDATE: What if you don’t want to rely on an exception to determine whether the transaction is successful?

In general, I tend to think of a failed transaction as an exceptional situation. I generally assume transactions will succeed and when they don’t it’s an exceptional situation. In other words, I’m usually fine with an exception being the trigger that a transaction fails.

However, Omer Van Kloeten pointed out on Twitter that this can be a performance problem in cases where transaction failures are common and that returning true or false might make more sense.

It’s trivial to provide an overload that takes in a Func<bool>. When you use this overload, you simply return true if the transaction succeeds or false if it doesn’t, which is kind of nice. Here’s an example of usage.

Transaction.Do(() => {

  DoSomeWork();
  if(SaveWorkToDatabaseSuccessful()) {
    return true;
  }
  return false;
});

The implementation is pretty similar to what we have above.

public static void Do(Func<bool> action) {
  using (TransactionScope transaction = new TransactionScope()) {
    if (action()) {
      transaction.Complete();
    }
  }
}
Found a typo or error? Suggest an edit! If accepted, your contribution is listed automatically here.

Comments

avatar

48 responses

  1. Avatar for Scott
    Scott August 18th, 2009

    That's a nice idea and one that is used in good JavaScript code all the time.

  2. Avatar for Peter Lanoie
    Peter Lanoie August 18th, 2009

    This seems like a logical implementation of a transaction. After all, if you are explicitly using a transaction isn't the general intent to commit it?
    How will this behave if I do an explicitly rollback from within the transaction's Action? Transaction.Complete() must respect a previous rollback() I imagine. (Haven't worked with txs much.) So after an explicit Rollback() the implicit Complete() really has nothing to do so it can still execute.

  3. Avatar for Ian Suttle
    Ian Suttle August 18th, 2009

    Funny you bring this up. I was working with TransactionScope the other day and had forgotten to Complete() it. You'd have save'd me some headaches there :).
    While I like the creative aspect of shortcutting the process I find the original implementation easier to read and therefore easier to maintain.

  4. Avatar for Steve Smith
    Steve Smith August 18th, 2009

    I like it. I don't use transactions all that often, but this seems to me to be the same kind of simplification that saves bugs as when we moved from ADO to ADO.NET and replaced
    while Not RS.EOF
    ...
    RS.MoveNext 'Do not forget this line!
    end while
    to:
    while(myDataReader.Read()
    {
    ...
    }
    Eliminating the need for the last (required) call in the pattern is definitely a good thing.

  5. Avatar for Eduardo Campa&#241;&#243;
    Eduardo Campa&#241;&#243; August 18th, 2009

    Nice, less code, i like that

  6. Avatar for Colin Whitlatch
    Colin Whitlatch August 19th, 2009

    I use the same technique ( passing actions around ) forgeneric exception handling ( just logging! ) & launching simple threads. I hadn't thought about doing it with transactions, I like it!

  7. Avatar for Paco
    Paco August 19th, 2009

    Personally, I would not use it. I wrap the transaction in a unit of work. For 99% of the cases, I start a transaction on the begin of a webrequest, and commit it at the end.
    With the implementation you use, you have to manage the transaction all over the application, everywhere where data-access is used. In a typical application, just one (or two or three) places are enough.

  8. Avatar for Michael Monteleone
    Michael Monteleone August 19th, 2009

    I have been using this technique for years, though never blogged about it. When anonymous delegates (and later lambdas) were released, database transactions seemed a textbook use-case.
    Another great use is memoization, backed by what-have-you. (Cache, Session, HttpContext items)
    SomeType someMemoizedItem = LazyCache.Fetch<SomeType>("cacheKey", ()=> {
    // perform expensive action here
    return new SomeType();
    });
    Wherein Fetch() does the lifting of checking if the item is cached, and if not creating it and caching it.

  9. Avatar for David Mohundro
    David Mohundro August 19th, 2009

    I saw this technique used the first time by Venkat Subramanium at www.agiledeveloper.com/.../PermaLink.aspx. His code wasn't wrapping transactions, just IDisposable. Still, a nice technique that functional programming can provide.

  10. Avatar for Dummy Customer
    Dummy Customer August 19th, 2009

    I don't like it!
    I think that your new solution is less readable than the initial code.
    This is an extra abstraction layer that causes the code to be less readable and also less easier to debug.
    The debugging of your code becomes more difficult, because an exception will bubble in the Do() method. So Visual Studio will break in the Do() method. So the only way to actually see where the exception occurred is to go through the stack trace.
    In essence, you want an exception to hit as close to the actual code that caused it. And yes, I know that can be difficult to achieve.
    Also, Transactions are usually used in business apps.
    All transactions usually go through a Save() method in your business app. Having multiple transactions and transaction scopes throughout a business app is usually a bad sign.
    The Save() method is the uber method that does saving, transactions, logging, exception bubbling, translation of exception message etc.
    Applying your solution to the Save() method will decrease that method with one line, but it will also add additional indirection and complexity where it is not needed.
    So conclusion:
    It's always good to think about code reduction, but it has to make sense in a real world solution and I don't see this having a real world benefit.

  11. Avatar for Kevin
    Kevin August 19th, 2009

    Maybe you can send parameter to delegate to configure TransactionOptions, like IsolationLevel and Timeout, that are important configurations at Transaction level.

  12. Avatar for Jos&#233; Romaniello
    Jos&#233; Romaniello August 19th, 2009

    We have something like this for testing purposes at unhaddins. (enclose the nh session and transaction)
    Look this http://digg.com/u1B1bT.

  13. Avatar for Ragan Martin
    Ragan Martin August 19th, 2009

    Simple and understandable Code works better than saving a couple of lines in code, saving 10 lines of codes sometimes means 30 minutes of explanation for a new developer in my team.
    Use code that everybody understands and you'll be more productive.

  14. Avatar for Michael Teper
    Michael Teper August 19th, 2009

    A small nitpick, but I would have named it Transactional.Do(), which I think reflects what's going on slightly better.

  15. Avatar for Scott Bellware
    Scott Bellware August 19th, 2009

    Yep, used to do this back in the day :)
    The problem with this (and all uses of the dispose pattern for anything other than it's original intention) is that you have to know the code inside the Dispose method, or in this case, the Do method.
    It's inevitably a violation of encapsulation.
    Implicit semantics remove lines of code from code, while simultaneous removing meaning. The one doesn't do justice to the other.
    Ultimately, forgetting to write the line of code to complete a transaction isn't much of a productivity problem. It would have been readily discovered by the functional tests for the feature.
    You do write your own functional tests, don't you :)

  16. Avatar for Tobin Harris
    Tobin Harris August 19th, 2009

    I always forget to commit transactions, and since I've been doing it for 14 years, I think this makes for great discussion!
    The using pattern is great for enforcing safe disposal or resources, but I suspect it's orthogonal to transaction commit/rollback?
    With that out of the picture, then I personally prefer to fall back on the rule of least surprise. For me, I always like code to read in such a way that it prioritises the best-case-scenario. This means that ransactions should commit by default.
    In SQL Server Managment Studio this is the default setting - you execute SQL and it automatically commits. On the other hand, last time I used Oracle SQL tools, you had to explicitly commit your work.
    So, for some developers, transactions should rollback by default. For other developers, transactions should complete by default. It's cultural.
    The explicitness of transaction.Complete() is great when there is no standard way of doing things. And I think there is no standard way of doing things. So, I say we need to be explicit and write transaction.Complete() in our code, rather than relying in a convention.

  17. Avatar for Tobin Harris
    Tobin Harris August 19th, 2009

    Actually, I concluded badly there. I think the using block should throw an exception if the transaction is neither committed or rolled back.

  18. Avatar for Scott Bellware
    Scott Bellware August 19th, 2009

    Tobin,
    Throwing an exception in a using block is something that, while not verboten to the pattern, I would consider to stretch least surprise uncomfortably.

  19. Avatar for Haacked
    Haacked August 19th, 2009

    @bellware Interestingly, I think the current behavior of transaction scope is weird. When I see a using block, I naturally think of it as defining a scope. And within that scope, something is different.
    For example, within a lock statement, I expect everything inside to be governed by the lock. When you leave the lock block, I expect the lock to be released.
    So I find it odd that I have to call transaction.Complete() at all. I kind of think it's only there to prevent the transaction from being disposed of prematurely, not sure though.
    That's how I read the Do method. Everything in there is part of the transaction.
    In fact, the original intention of the Dispose pattern was to delineate and dispose of a scope, not just harmful resources. At least that's what Eric Gunnerson once told me ;).


    Not only that, the TimedLock demonstrates what the C# team had in mind with the using statement. It wasn't intended just for cleanup, but for situations just like this.
  20. Avatar for Michael Monteleone
    Michael Monteleone August 19th, 2009

    I understand the concerns that this hides explicitness, but I think in this case it's OK, especially as the intent is still obvious and the hidden code is boilerplate. For the record, Ruby on Rails also uses an identical approach:
    ActiveRecord::Base.transaction do
    david.withdrawal(100)
    mary.deposit(100)
    end
    That being said, this discussion smells particularly bike-sheddy.

  21. Avatar for alberto
    alberto August 19th, 2009

    There is something similar in Ayende's Rhino.Commons.
    You can see an example here

  22. Avatar for Neal Blomfield
    Neal Blomfield August 19th, 2009

    Tobin's idea sounds much more appealing.
    public static void Enforce(Action[TransactionScope] action)
    {
    using (TransactionScope transaction = new TransactionScope())
    {
    action(transaction);
    var txStatus = Transaction.Current.TransactionInformation.Status;
    if( txStatus == TransactionStatus.InDoubt )
    {
    throw new InDoubtTransactionException();
    }
    }
    }

  23. Avatar for Johnvpetersen
    Johnvpetersen August 19th, 2009

    Re: @haacked's comment about
    having to invoke complete, completing a tx
    should be an explicit, not an implicit act. The
    using is just about what is on scope.
    DB actions are a different thing altogether.
    I like your approach.

  24. Avatar for zvolkov
    zvolkov August 19th, 2009

    SpringFramework.NET supported this for years with its TransactionTemplate.Execute(delegate(ITransactionStatus status))
    However in practice, I found it less than convenient, 1) vulnerable to the modified closure problem, 2) can't return from function within the delegate 3) can't yield within the delegate etc.
    AOP (w/ Transactional attribute) is the most convenient way. But using is the most universal. I agree that having to commit transaction is a pain.

  25. Avatar for Oskar
    Oskar August 19th, 2009

    I use this approach everywhere there's boilerplate resource handling involved: transactions, caching, http clients, as clients, etc. I'm not sure it qualifies as a pattern; It's only a callback parameter.

  26. Avatar for Joe Chung
    Joe Chung August 19th, 2009

    Nitpicking here but instead of
    if(SaveWorkToDatabaseSuccessful()) {
    return true;
    }
    return false;
    Why not
    return SaveWorkToDatabaseSuccessful();

  27. Avatar for Anders Lybecker
    Anders Lybecker August 19th, 2009

    The Transaction.Do is similar to what Microsoft Research came up with when transactional memory - STM.NET (msdn.microsoft.com/en-us/devlabs/ee334183.aspx)
    They use:
    Atomic.Do(()=>
    {
    i = 1;
    });
    :-)
    Anders

  28. Avatar for Lee Englestone
    Lee Englestone August 19th, 2009

    Very Interesting
    -- Lee

  29. Avatar for Patrik H&#228;gne
    Patrik H&#228;gne August 19th, 2009

    This is a technique I've been using for ages, coming from a functional background and it's great. I blogged about this pattern quite some time ago ondevelopment.blogspot.com/.../...e-functions.html, to me it's about composition, it's not about saving a line of code or two. The forgetting about closing the scope will be caught by your tests. Not only as Bellware says by functional tests but by unit tests as well as far as I'm concerned. http://legendtransactions.codeplex.com/

  30. Avatar for Harry M
    Harry M August 19th, 2009

    Reminds me of my "Try catch in a single line of code" post, which wasn't a great idea in retrospect, evn if people liked it. www.adverseconditionals.com/.../...ne-of-code.html
    The syntax already seems easy enough to use with TransactionScope. If your policy is to use this instead of TransactionScope, you now have a maintenance issue of making sure all your code can access. it. Another one for the .Commons library?

  31. Avatar for Luis Abreu
    Luis Abreu August 19th, 2009

    I've been using similar code to that for some time now (but since I tend to use NH, I tipically use the ITransaction interface). You could also consider passing the transaction object to the action method so that it has control over the final result.

  32. Avatar for Vito Botta
    Vito Botta August 19th, 2009

    Looks tidy and cute, but how do you use it if you want to return the results of a query to the outer function?
    I use TransactionScope also to simply get data (not only to persist changes), because this way I can set the IsolationLevel to ReadUncommitted and have a behaviour similar to NOLOCK. In many cases this Isolation Level is perfectly OK for me and by doing this I have seen ~20/25 faster queries.
    Your little snippet is cute, but I can I use it in this case?
    Say I have now something like
    using (TransactionScope transaction = new TransactionScope( TransactionScopeOption.Required, new TransactionOptions{ IsolationLevel = System.Transactions.IsolationLevel.ReadUncommitted}))
    {
    ...
    return (from ....);
    }
    Which is OK. But if I use your snippet, I have something like
    Transaction.Do(() => {
    return ..my query...
    });
    I can add of course an overload to be able to specify the isolation level, but in this case a boolean is expected instead of the results of my query.

  33. Avatar for RichB
    RichB August 19th, 2009

    I wrote the same thing recently for an enterprise system I work on. The prototypes are:
    public static T ExecuteInTransaction<T>(this ISession session, Func<T> lambda)
    and
    public static void ExecuteInTransaction(this ISession session, Action lambda)
    Note:
    1) They are extension methods on NHibernate's ISession
    2) There are 2 methods: one allows you to return the result of the lambda, the other is a void result.

  34. Avatar for Steve
    Steve August 19th, 2009

    @Vito:
    What a stupid comment, you are using the transaction without committing (just reading) so you wouldn't need this at all...

  35. Avatar for sean
    sean August 19th, 2009

    Why would you do this instead of writing a stored procedure that manages the transaction itself?
    There is only one case in which I have found stored procedures to be... difficult... to use, and that is when one of my logical parameters is an array of values. Tag querying is such a problem, but there are definitely database-centric ways around it that do not require writing SQL in client code strings somewhere.

  36. Avatar for yemek tarifleri
    yemek tarifleri August 19th, 2009

    If transaction failure a common I think there is another issue there.
    Throwing exception is better for me.

  37. Avatar for Javier Lozano
    Javier Lozano August 19th, 2009

    I really like the simplicity and exactness of the code. One thing, let's not add a Transaction ActionFilterAttribute to MVC. :)

  38. Avatar for David Meyer
    David Meyer August 20th, 2009

    I've used this too, but with a custom system I designed very similar to System.Transactions to implement transactionality when using a specific third-party library.
    I agree that to save one line of code may not be worth other considerations. But I have also forgotten to commit transactions, which is a bigger problem. Currently, if you forget to do this, it will not show up as a compile-time or run-time error. It will only have the effect of never commiting that transaction. But even assuming that you always write a functional test to verify the code, wouldn't it be better to catch it sooner? Say, at compile-time? Especially since some systems can have functional tests that take a long time to execute.
    Basically, the best solution would be to enforce in the language that before execution control leaves the block, the transaction has to be explicitly either committed or rolled back. This could easily be done in your solution by using Func<TransactionResult> instead of Action for the delegate (where TransactionResult is just an enumeration, could be called soemthing else), which would require the delegate to return whether or not to commit or rollback the transaction before it completes.
    Or the problem could be solved even simpler by incorporating the fact that it will be committed into the wording of the function.

  39. Avatar for Pat Gannon
    Pat Gannon August 20th, 2009

    I like how the code looks, but with regard to applying this approach broadly: how could you inject a mock or a fake of TransactionScope from a unit test (which doesn't touch the DB)? There are scenarios where all of the DB updates resulting from a particular piece of business logic should be applied together in a transaction, and testing such logic without touching the database is ideal IMO.
    Using TransactionScope directly, you could inject a TransactionScopeBuilder (which could be mocked) and then use that to instantiate your TransactionScope. You could use the same approach with your Transaction.Do (and pass in the builder to the Do call), but it might make for somewhat awkward syntax, which might reduce the usefulness of this approach.

  40. Avatar for Haacked
    Haacked August 20th, 2009

    @sean Several reasons.
    1. You're not using stored procs but using some sort of OR/M.
    2. You're doing a distributed transaction across multiple databases or data stores.
    3. You're writing code that needs to be database agnostic (like a product which runs against different databases).

  41. Avatar for Hemanshu Bhojak
    Hemanshu Bhojak August 20th, 2009

    If I am returning true or false I will rather use transaction.complete().
    In case I want to swap a function in and out of a transaction I find your solution useful as it is transaction independent.
    So I simply do Transaction.do(myfunc) if I want it to run in a transaction.
    It does make it simple in some cases. :)

  42. Avatar for Vito Botta
    Vito Botta August 20th, 2009

    @Steve
    If you had actually read my "stupid" comment before adding your "clever" one, you would -hopefully- understood why I use TransactionScope, in some cases, also to retrieve data.
    It's about avoid the locking when this is OK as this helps speed up your queries.
    Which part of
    "I use TransactionScope also to simply get data (not only to persist changes), BECAUSE this way I can set the IsolationLevel to ReadUncommitted and have a behaviour similar to NOLOCK. In many cases this Isolation Level is perfectly OK for me and by doing this I have seen ~20/25 FASTER queries"
    wasn't clear enough?

  43. Avatar for andyclap
    andyclap August 20th, 2009

    I've been using this for a while, not for compactness or to guard against forgetting to complete, but for separation of concerns.
    I don't want to know how to run a transaction, all I want to know is that a bit of work should be atomic, viz (c#2):
    public void DoStuffAndOtherStuffTogether()
    {
    atomicCoordinator.Do(delegate
    {
    stuff();
    otherstuff();
    });
    }
    Stems from adapting a project using fugly dbConnection transactions to using TransactionScope. And it helped when testing as I can set up a mock to ensure a bit of work was atomic rather than trying to fail a bit of the transaction and testing it all rolled back.
    @sean - sprocs vs app code is a whole different debate!
    @vito - you could use this way of doing things too:
    MyStuff GetStuff()
    {
    return uncommittedOptimisationIsFineForThis.FetchOf<MyStuff>(delegate
    {
    return GetMyStuff();
    });
    }
    Means that if you need to change technologies, the thing that knows how to apply uncomitted optimisations via a transaction scope can decide whether to use it or not (or use a different technique), rather than your code.
    The messier problem as zvolkov mentioned above is c#'s closures, you have to assume the delegate is going to be called and forgotten, and turn off the warning, hey ho.

  44. Avatar for Thanigainathan
    Thanigainathan August 20th, 2009

    That sounds good. But during the nested transactions does this behaves equally ?
    Thanks,
    Thani

  45. Avatar for Pita.O
    Pita.O August 24th, 2009

    @Thanigainathan: This is really the System.Transactions imperative call leveraging lambda to void repeating a single, required line of code. Nested transaction scopes always vote to commit but do not commit on their own. Only top level scope.Complete() does the commit. This construct will work the same way.
    I saw this construct once (forgotten where) and thought it was cool ... It doesn't just save one line of code. It save a few more: boiler-plate IConnectionString, IDatabaseGateway, ResultDto (return value) construct that is repeated everywhere in the Tasks layer.

  46. Avatar for Matt
    Matt August 26th, 2009

    I recently wrote a short article on a more generic approach of wrapping up "using" statements like this. You can check it out here: craftycoders.com/.../...xtension-Methods-in-C.aspx

  47. Avatar for Rick Dailey
    Rick Dailey August 31st, 2009

    This reminds me of my DataBind helper (because for some reason, I feel like DataBind is a single op 90% of the time).
    public static void DataBind(this BaseDataBoundControl control, object dataSource) {
    control.DataSource = dataSource;
    control.DataBind();
    }

  48. Avatar for Norville
    Norville September 18th, 2009

    Why do people feel the need to abstract things that don't need abstracting? For 3 lines of code, and you have intellisense, is it really that hard? Think of the poor shmuck (typically me) that has to come in and maintain/fix it...
    If you want to save space, then stop using braces for one line IF statements. At least I can easily understand. :-P
    (I actually do like the blog, good info even if a little abstract.)