A Simple Closure To Handle Try/Catch Around Transactions

(Updated: I moved the begin transaction outside of the try as Chad suggested in the comments.)

If you’re like me, you’re lazy and hate putting try/catch around your transaction handling in your code. It has to be there, but it’s just a pain. You may have code that looks something like:

domainContext.BeginTransaction();

try
{
    historicalPwdService.RecordHistoricalPassword(user.UserProfileID, currentPassword);

    user.Password = newPassword;
    user.PasswordCreateDate = systemClock.Now();

    userProfileRepo.Save(user);

    domainContext.CommitTransaction();
}
catch
{
    domainContext.RollbackTransaction();
    throw;
}

Well here’s a little helper class that can ease the pain a bit:

public static class WorkUnit
{
    public static void Do(IDomainContext context, Action workUnit)
    {
        context.BeginTransaction();

        try
        {
            workUnit();
                
            context.CommitTransaction();
        }
        catch
        {
            context.RollbackTransaction();

            throw;
        }
    }
}

now you can use it with an anonymous method:

WorkUnit.Do(domainContext, ()=>
    {
        historicalPwdService.RecordHistoricalPassword(user.UserProfileID, currentPassword);

        user.Password = newPassword;
        user.PasswordCreateDate = systemClock.Now();

        userProfileRepo.Save(user);
    });

Side note for Jimmy: You should be proud. I went back and switched out my custom delegate for Action. ;)

Technorati Tags: ,

Related Articles:

Post Footer automatically generated by Add Post Footer Plugin for wordpress.

About Ray Houston

Ray is a software development leader and architect with 20 years hands-on experience. He enjoys finding elegant solutions to complex problems and delivering real value to customers. Ray is a speaker and contributor to community events.
This entry was posted in Uncategorized. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

21 Responses to A Simple Closure To Handle Try/Catch Around Transactions

  1. shawn Hinsey says:

    Great pattern. I use a .net 2.0 version to implement a circuit breaker (I call it fail fast) that you can fairly transparently use to wrap remote calls.

  2. Chad Myers says:

    Cool stuff.

    1.) Shouldn’t BeginTransaction be outside the try? What if it fails, then you attempt to roll it back?

    2.) It might be neat to make this an extension method on IDomainContext instead of a static class. You’re 90% there. That way you could do something like:

    domainContext.DoWorkUnit(()=>
    {
    // work here
    });

  3. Of course, I would be interested in seeing a .NET 2.0 version of the code that is compatible with Mono :)

  4. Ray Houston says:

    @Chad –
    1) yeah that would be better. I actually handle that within domainContext, but moving it out of the try would be clearer. I’ll update the example.
    2) I thought about that but domainContext is really an IDomainContext and it’s mocked when testing. It seemed wrong to have to have to do an Expect.Call().Do everywhere to make it run the Action. I could create a stub I guess, but then I’m testing the stub. Thoughts?

    @Chris – You can replace the ()=> with the word ‘delegate’ and it should run in 2.0.

  5. I prefer the Scope syntax with the using keyword:

    using (var context = domainContext.BeginTransaction())
    {
    //do stuff
    scope.Completed = true;
    }

    In Dispose, you Rollback or Commit depending on the state of Completed. It is a lot more fluent than having delegates and is totally 2.0 compatible.

  6. Ray Houston says:

    @Francois – I’ve used that in the past, but what I like about the delegate way is that you don’t have to put context.Completed = true. If you didn’t have to put that, then I would agree, but being required to put it makes it less fluent.

    Maybe in C# 4 we’ll be able to omit the ()=> { } when there are no parameters and just have the { }. ;)

  7. Ray Houston says:

    @Chad – you know, I didn’t read your comment carefully (early in the morning) and I missed the “extension” part. A few minutes ago thought about extension methods and was going to put an update, but then reread your comment. Great idea!

  8. Chad Myers says:

    Ray, after reading this again, I think I might tend to agree with Francois, except for the the completed flag. I like the more expressive ‘CommitTransaction()’.

    Either with or without the IDisposable stuff is fine, I’m not trying to nitpick, just exploring different styles to see if one might be more obvious/soluble or whatnot.

    How does this catch you?

    public static void Do(this IDataContext context, Action workUnit)
    {
    using( ITransaction trans = context.BeginTransaction() )
    {
    workUnit();
    trans.Commit();
    }
    }

  9. Ray Houston says:

    Sure, that’s cool for the extension method. If you’re talking about doing that in the consumer code, then I would ask when do you ever not commit the transaction. For me, it’s only during an unhandled exception. I shouldn’t have to put the commit because it is implied by being a unit of work. I can see the advantage of the IDisposable over my try/catch, but not sure that it has an advantage over the delegate method. I think your example that uses both is the best. That makes the extension method explicit and client code pretty fool proof.

  10. Ken Scott says:

    Do you have a fuller example of your Domain Context? I’m curious what kind of things live in there.

  11. Ray Houston says:

    @Ken – There’s not really much there. It’s has the transction and flush methods as well as a reference to a User (IPrinciple) object that executing the current thread. In the places where I need a full ISession (mainly in Repos), I pass in the IDomainContext and it looks for a RawDataSession property (typeof(object)) where it will attempt to cast to ISession. It keeps the ISession out of most of the domain code. I could probably do a blog post about it if anyone is interested.

  12. Evan says:

    You can also go this route (warning, blog code):

    With.Transaction(delegate {
    //do something
    });

    public delegate void VoidDelegate();

    public class With
    {
    public static void Transaction(VoidDelegate work)
    {
    using(ITransaction trans = context.BeginTransaction())
    {
    work();
    trans.Complete();
    }
    }
    }

    I’m omitting the dependency stuff for context, but you should get the gist. You can do this in 2.0, or spiffy it up with 3.0 features if you like.

    I’ve seen people refer to this as the Hole in the Middle pattern.

  13. Chad Myers says:

    In .net 3.5, please use the System.Action delegate though, and don’t make a new one (VoidDelegate). But for .NET 2.0, that’s what you have to do.

    @Evan: How do you get the context into the With() class and/or the Transaction() function?

  14. Evan says:

    @Chad

    There are a couple ways. Considering transactions are typically managed by the Service Layer, you can finagle it in through there.

    I’m assuming there’s some type of IContextFactory or something:

    public interface IMyService
    {
    void DoSomething();
    }

    public class MyService : IMyService
    {
    public MyService(IDbContextFactory factory)
    {
    _dbFactory = factory;
    }

    private IDbContextFactory _dbFactory;

    protected IWith With
    {
    get { return new WithHelper(_dbFactory.Create()); }
    }

    public void DoSomething()
    {
    With.Transaction(delegate {
    //do transactional work
    });
    }
    }

    At which point you’d convert the Transaction method of my previous example into an instance member, instead of a static one.

    public interface IWith
    {
    void Transaction(VoidDelegate work);
    }

    public class WithHelper : IWith
    {
    public WithHelper(IDbContext context)
    {
    _dbContext = context;
    }

    private IDbContext _dbContext;

    public void Transaction(VoidDelegate work)
    {
    using (IDbTransaction tx = _dbContext.CreateTx())
    {
    work();
    tx.Complete();
    }
    }
    }

    You can also move the With property onto an abstract layer supertype if you like. It will make constructor injection tricky though, you might look at Setter injection instead (which I don’t prefer, but it’s a choice). Also make sure you are taking advantage of IDisposable (since we are talking about resources which should be properly managed and disposed of).

    You can take this same approach (using a delegate to fill a hole in the middle) to create an alternative to a Template Method. I built one recently (but not for transactions), where their was a fluent api wrapping the helper (ie..the Transaction(VoidDelegate work) returned an interface)..

    In my case, It worked really well for enforcing the DRYness of some error handling code that was needed in multiple places.

  15. Ray Houston says:

    @Evan – something feels dirty about having to create an instance method of With on MyService. You’d have to create that on every class that you use it on. Wouldn’t that be anti DRY?

  16. Chad Myers says:

    @Evan: That seems like an awful lot of code and indirection just to get the With syntax.

    Since this, so far in this discussion, is solely for the With.Transaction() usage, what if you just had an extension method on IDataContext:

    context.WithTransaction(()=>
    {
    // blah blah
    });

  17. Evan says:

    @Ray

    Fluenty things often involve a compromise in terms of design purity. It’s a tradeoff.

    You can move the With property onto a layer supertype to clean up the dryness (since service layer classes manage transactions), but that’s a tradeoff on the DI side, since constructor injection in a base class is messy.

    If you want to see a similar design, you can see Fowler’s writeup on the Notification Pattern. His usage of failIfNullOrBlank() on the layer supertype is IMO a very similar design (as the With), but without delegates of course.

    http://martinfowler.com/eaaDev/Notification.html

    @Chad

    I haven’t moved to C# 3.0 land yet. I’m still in 2.0. :-)

    I do have to say that the non-lambda approach has less noi()s=>e..

    Also, trying to cram a lambda where an anonymous method is natural seems a bit awkward to me.

  18. Ray Houston says:

    @Evan – I see where you’re coming from but I’m not sure I agree in this case. Saying With.Transaction may be slightly prettier than context.WithTransaction (or whatever your term is) but it doesn’t seem worth the trouble.

    BTW, You could make IDomainContext implement IWith. Then you could return that instance from your With property and get rid of the helper class.

  19. Chad Myers says:

    @Evan – RE: Lambda
    From a technical purity and lambda calculus perspective, I can see how it might be offensive. But when you start using them extensively like Jeremy and I are doing, having to put ‘delegate’ all over the place becomes obviously worse. Especially when you have multiple arguments to the anonymous method and you have to explicitly call them out:

    delegate(Product p1, Product p2){ return p1.Code.CompareTo(p2.Code); }

    vs.

    (p1, p2)=> p1.Code.CompareTo(p2.Code);

  20. Ray Houston says:

    @Chad & @Evan – When I first worked with lambdas all I saw was noise, but it was because I wasn’t used to seeing them. Hell, when I first looked at Ruby I thought the same thing. After working with lambdas just a little, the ‘delegate’ keyword became the noise.

  21. This is a great pattern! I have been using something like it since anonymous delegates came out. I’m sure you know this, but there are many other circumstances where you can apply this pattern. I find it handy for encapsulating repetive loops where I do some sort of pre- or post- operations on some collection of objects (over and over again).

Leave a Reply

Your email address will not be published. Required fields are marked *

*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>