November 2009 - Posts

In an earlier post I talked about writing a Transaction attribute for MVC using NHibernate (though it isn’t really NHibernate specific).  The basic idea is that when an action marked with [Transaction] is executing (OnActionExecuting) you begin a transaction, and some time later (I use OnActionExecuted, but Kazi Manzur Rashid makes a good argument for OnResultExecuted) you commit that transaction if there was no error, or rollback if there was an error.
 
With that in mind, the implementation details aren’t really important because my motivation in this post is to make every action transactional by default.  Most of the actions on any real site will require retrieving, updating, saving or removing data so as a best practice I would like the user to have to specify if they choose not to use a transaction (maybe for a static about page).
 

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:

public ActionResult Index()
{
    var data = //get data
    return View(data);
}

Now if we explicitly use a [Transaction] attribute it should still execute in a Transaction:

[Transaction]
public ActionResult Index()
{
    var data = //get data
    return View(data);
}

However, we can choose to use a self-defined [HandleTransactionManually] attribute which will Not Use a Transaction:

[HandleTransactionManually]
public ActionResult Index()
{
    var data = //get data
    return View(data);
}

 

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:

[AttributeUsage(AttributeTargets.Class, AllowMultiple = false)]
public class UseTransactionsByDefaultAttribute : ActionFilterAttribute
{
    private IDbContext _dbContext;
    private bool _delegateTransactionSupport;
 
    public IDbContext DbContext
    {
        get
        {
            if (_dbContext == null) _dbContext = SmartServiceLocator<IDbContext>.GetService();
 
            return _dbContext;
        }
    }
 
 
    public override void OnActionExecuting(ActionExecutingContext filterContext)
    {
        _delegateTransactionSupport = ShouldDelegateTransactionSupport(filterContext);
 
        if (_delegateTransactionSupport) return;
 
        DbContext.BeginTransaction();
    }
 
    public override void OnActionExecuted(ActionExecutedContext filterContext)
    {
        if (_delegateTransactionSupport) return;
 
        if (DbContext.IsActive)
        {
            if (filterContext.Exception == null)
            {
                DbContext.CommitTransaction();
            }
            else
            {
                DbContext.RollbackTransaction();
            }
        }
    }
 
    private static bool ShouldDelegateTransactionSupport(ActionExecutingContext context)
    {
        var attrs = context.ActionDescriptor.GetCustomAttributes(typeof (TransactionalActionBaseAttribute), false);
 
        return attrs.Length > 0;
    }
}

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:

public class TransactionalActionBaseAttribute : ActionFilterAttribute
{
    
}

 

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

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public class HandleTransactionManuallyAttribute : TransactionalActionBaseAttribute
{
}

 

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

public ActionResult Index()
{
    var data = //get data
    return View(data);
}

image

 

 

 

 

Situation Two – We specify a transaction attribute and we get the exact same result

[Transaction]
public ActionResult Index()
{
    var data = //get data
    return View(data);
}

image

 

 

 

 

Situation Three – We specify HandleTransactionManually and we get no transaction

[HandleTransactionManually]
public ActionResult Index()
{
    var data = //get data
    return View(data);
}

image

 

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).

Enjoy!

 
      Telerik recently released their Extensions for ASP.NET MVC which include several great controls, the most immediately useful of which is their Grid control.  In the simple (and probably most common) cases the grid works by passing taking an enumerable list of objects (let’s say Northwind Orders for this example) and rendering this out as a table based on a fluent configuration.  The benefit of passing in an enumerable list is that Telerik’s Grid will use its data engine and dynamically page, sort and filter your data on the server (aka: at the database), which is a huge win.  However, by allowing the Grid to handle your actual database call you lose control of transactional support and you start using what Oren Eini refers to as “implicit transactions”.  For more information on implicit transactions and why you shouldn’t use them see http://nhprof.com/Learn/Alert?name=DoNotUseImplicitTransactions.
 

Default Grid Behavior Implementation

Following the default example on Telerik’s site, we get a configuration similar to this:
 
[The Order Controller’s List() Method]
public ActionResult List()
{
    var orders = _orderRepository.Queryable; //Return IQueryable<Order>
 
    return View(orders);
}

[The List View]

<%= Html.Grid(Model)
    .Name("Orders")
    .Columns(col =>
        {
            col.Add(order => order.OrderedBy.CompanyName);
            col.Add(order => order.ShipAddress);
            col.Add(order => order.OrderDate).Format("{0:d}");
        })
    .Pageable(x=>x.PageSize(20))
    .Sortable()
%>

 

Default Grid Behavior Results

When you run this example, you get two nice queries – one which does a count of all orders for paging purposes and one which selects the correct 20 orders from the database (sorted correctly).  You can see this in NHProf below:
 
image 
 
This all looks pretty nice, but of course those alerts are because of implicit transactions.  Let’s try adding a transaction attribute to our wrap our action in a transaction:
 
[Transaction]
public ActionResult List()
{
    var orders = _orderRepository.Queryable; //Return IQueryable<Order>
 
    return View(orders);
}
 
Running this again, we get the following:
 
image
 
Now we are getting a transaction but since we aren’t querying for the data until the action has returned the queries still take place outside of the transaction. Let’s try to fix that.
 

Transactional Options for the Grid

The general idea for wrapping the grid’s query in a transaction is that you have to open the transaction before the grid executes its render and close the transaction after it is done.  Extension methods could be a candidate here, but Render() returns void so you can’t do a commit, and regardless, for the basic grid example you don’t call render directly anyway.  Another option would be to wrap the grid in a using(transactionScope){…} method similar to using(Html.BeginForm()){…} but this seems excessive.  Or you could simply make html helpers which would begin/end transactions, but I would hate to have that in my view (I don’t think that’s the view’s responsibility).

So, with that in mind, I set off to see if I could hook into the render method and wrap it in a transaction manually.  After pouring through the Telerik source code (the extensions are on Codeplex here) I learned a few things.  First, calling Html.Telerik() causes some fairly intense control initialization code which I wouldn’t want to rewrite.  Second, calling Html.Telerik().Grid() returns an instance of GridBuilder<T>.  Finally, GridBuilder<T> has virtual methods which allow overriding and give me the hook I need to add in transactional support.

 

Creating the CustomGridBuilder<T> class

To create my custom grid builder I’ll just subclass GridBuilder<T> and add a single method – Transactional().  In the interest of space Transactional() will return a GridBuilder<T> instance so it must be called as the first method after Grid(). The full implementation is below:
 
public class CustomGridBuilder<T> : GridBuilder<T> where T : class
{
    protected bool UseTransaction { get; set; }
 
    public CustomGridBuilder(Grid<T> component) : base(component)
    {
        UseTransaction = false;
    }
 
    public GridBuilder<T> Transactional()
    {
        UseTransaction = true;
 
        return this;
    }
 
    public override void Render()
    {
        if (UseTransaction)
        {
            using (var ts = new TransactionScope())
            {
                base.Render();
 
                ts.CommitTransaction();
            }
        }
        else
        {
            base.Render();   
        }
    }
 
The big points here are that transactions are optional by default (in case you want to use custom binding, json, anonymous types, etc), and calling Transactional() sets the UseTransaction bool to true.  This causes the render method to be wrapped in a transaction scope if necessary.
 
Now we need a way to return our CustomGridBuilder<T> instead of GridBuilder<T>, so we’ll create some HtmlHelperExtensions.  You can create one for each overload of Html.Telerik().Grid() if you want, but for this example I’m only going to override the IEnumerable<T> option.
 
public static class HtmlHelperExtensions
{
    public static CustomGridBuilder<T> Grid<T>(this HtmlHelper htmlHelper, IEnumerable<T> dataModel) where T : class
    {
        var builder = htmlHelper.Telerik().Grid(dataModel);
 
        return new CustomGridBuilder<T>(builder);
    }
}

Notice here we initialize the Grid by calling the Telerik() initialization method.  This way if Telerik updates the initialization logic in future releases or even the GridBuilder logic we shouldn’t have to make any code changes at all.

We are almost done – let’s take a look at what those two classes have bought us.

 

Results!

Since we have an HtmlHelper called Grid<T> which takes an IEnumerable<T>, let’s use it to create the grid.  This code is very similar to the code we started with, adding one significant difference.
 
<%= Html.Grid(Model)
    .Transactional() //New method adding transactional support
    .Name("Orders")
    .Columns(col =>
        {
            col.Add(order => order.OrderedBy.CompanyName);
            col.Add(order => order.ShipAddress);
            col.Add(order => order.OrderDate).Format("{0:d}");
        })
    .Pageable()
    .Sortable()
%>

Of course here you can see the first method is Transactional(), and that returns a GridBuilder<T> so we can chain on any of Telerik’s methods without affecting functionality (and staying safe from future API changes).

Let’s run this example one more time (without changing) the controller at all.

image

Now that is some nice transactional SQL :)

Enjoy!

I wrote a post about a month ago about using xVal with NHibernate Validator 1.2 which solved a problem I was having upgrading the xVal ‘in-the-box’ provider to work with a newer version of NHibernate Validator. There was a caveat that my solution only worked for ValidatorMode.UseAttribute and I wouldn’t catch XML or Loquacious (or other?) validation.  This seemed to work OK, but Fabio Maulo wrote a comment to that post saying NHV has metadata which should be the same no matter which validation mode was used.

So I decided to investigate how I could get the metadata without resorting to the NHibernate.Validator.Mappings validationMode specific engines (I was using ReflectionClassMapping, but there are others like XmlClassMapping).

I think I found the solution (at least, it does work…) in the ValidatorEngine’s GetClassValidator method.  GetClassValidator takes in a System.Type and returns an IClassValidator for that Type.  Once you have the class validator, you can get constraints for any member of that class by using GetMemberConstraints(memberName).

After getting the constraints you are onto the easy part, which is basically just mapping the rules into xVal rules and the code is much the same as before.

So without further ado, here is the new ValidatorRulesProvider implementation:

public class ValidatorRulesProvider : CachingRulesProvider
{
    private readonly RuleEmitterList<IRuleArgs> _ruleEmitters;
 
    public ValidatorRulesProvider()
    {
        _ruleEmitters = new RuleEmitterList<IRuleArgs>();
 
        _ruleEmitters.AddSingle<LengthAttribute>(x => new StringLengthRule(x.Min, x.Max));
        _ruleEmitters.AddSingle<MinAttribute>(x => new RangeRule(x.Value, null));
        _ruleEmitters.AddSingle<MaxAttribute>(x => new RangeRule(null, x.Value));
        _ruleEmitters.AddSingle<RangeAttribute>(x => new RangeRule(x.Min, x.Max));
        _ruleEmitters.AddSingle<NotEmptyAttribute>(x => new RequiredRule());
        _ruleEmitters.AddSingle<NotNullNotEmptyAttribute>(x => new RequiredRule());
        _ruleEmitters.AddSingle<NotNullAttribute>(x => new RequiredRule());
        _ruleEmitters.AddSingle<PatternAttribute>(x => new RegularExpressionRule(x.Regex, x.Flags));
        _ruleEmitters.AddSingle<EmailAttribute>(x => new DataTypeRule(DataTypeRule.DataType.EmailAddress));
        _ruleEmitters.AddSingle<DigitsAttribute>(MakeDigitsRule);
    }
 
    protected override RuleSet GetRulesFromTypeCore(Type type)
    {
        var classMapping = new ValidatorEngine().GetClassValidator(type);
 
        var rules = from member in type.GetMembers()
                    where member.MemberType == MemberTypes.Field || member.MemberType == MemberTypes.Property
                    from constraint in classMapping.GetMemberConstraints(member.Name).OfType<IRuleArgs>()
                    // All NHibernate Validation validators attributes must implement this interface
                    from rule in ConvertToXValRules(constraint)
                    where rule != null
                    select new { MemberName = member.Name, Rule = rule };
 
        return new RuleSet(rules.ToLookup(x => x.MemberName, x => x.Rule));
    }
 
    private IEnumerable<Rule> ConvertToXValRules(IRuleArgs ruleArgs)
    {
        foreach (var rule in _ruleEmitters.EmitRules(ruleArgs))
        {
            if (rule != null)
            {
                rule.ErrorMessage = MessageIfSpecified(ruleArgs.Message);
                yield return rule;
            }
        }
    }
 
    private static RegularExpressionRule MakeDigitsRule(DigitsAttribute att)
    {
        if (att == null) throw new ArgumentNullException("att");
        string pattern;
        if (att.FractionalDigits < 1)
            pattern = string.Format(@"\d{{0,{0}}}", att.IntegerDigits);
        else
            pattern = string.Format(@"\d{{0,{0}}}(\.\d{{1,{1}}})?", att.IntegerDigits, att.FractionalDigits);
        return new RegularExpressionRule(pattern);
    }
 
    private static string MessageIfSpecified(string message)
    {
        // We don't want to display the default {validator.*} messages
        if ((message != null) && !message.StartsWith("{validator."))
            return message;
        return null;
    }
}

Of course in the global.asax file you just need to hook xVal and this class together like so:

xVal.ActiveRuleProviders.Providers.Add(
    new ValidatorRulesProvider(
    ValidatorMode.UseAttribute));

If you aren’t using xVal I would highly recommend it, and if you are using ASP.NET MVC and not using xVal, what are you waiting for?  They are a great match!

 

Enjoy!

More Posts