What I’d Like to Accomplish
Given any action method (we’ll use Index), if there is no attribute it should execute in a Transaction:
Now if we explicitly use a [Transaction] attribute it should still execute in a Transaction:
However, we can choose to use a self-defined [HandleTransactionManually] attribute which will Not Use a Transaction:
The UseTransactionByDefaultAttribute and the SuperController
To enforce “global” rules like this it is necessary for all controllers to inherit from a custom controller I call the “SuperController”. This is a class I was using anyway for helper/common methods and I assume a lot of other people don’t inherit directly from the Controller class as well. The SuperController just inherits from System.Web.Mvc.Controller for the purposes of this post.
Now I am going to start by making a class attribute for the SuperController (or any controller really) which will cause us to use transactions by default. First I’ll show the implementation and then explain it:
Explanation – So this is an action filter attribute of course, and it works only on classes. When the action executes (OnActionExecuting) I check to see if I should “delegate” the transaction support, and if so I’ll return and do nothing else. If I am not delegating transaction support, I’ll begin the transaction. The OnActionExecuted method does something similar and if we are not delegating it takes care of committing or doing the rollback depending on need.
The ShouldDelegateTransactionSupport is pretty interesting in that it check the custom attributes on the current action (using ActionDescriptor.GetCustomAttributes()) and if it sees any attributes that inherit from my custom TransactionalActionBaseAttribute class then it return true, meaning that we should delegate transaction support to the attributes.
This base class is pretty simple:
Handling Transactions Manually if Needed
If you want to override the default transaction then we need another attribute which will provide this support for us. Since we have seen that inheriting from TransactionalActionBaseAttribute means we are delegated responsibility for the transaction, we can just ask for responsibility and then do nothing with it, as below
Results – Show and Tell
Let’s see how this works for us.
Situation One – We don’t specify any transaction attribute and we get a transaction
Situation Two – We specify a transaction attribute and we get the exact same result
Situation Three – We specify HandleTransactionManually and we get no transaction
I’m a big fan of setting best practice defaults and default transactions are a good way to help get devs to use transactions as much as possible (which for many reasons is a good thing).