Inserting Transactions into Telerik’s ASP.NET MVC Grid

 
      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!

3 Comments

  • i'm interested to know how you've done Filtering with nHibernate and the telerik mvc grid. Any other cool stuff with nHibernate and the telerik MVC grid?

    Good stuff.

  • @andrewboudreau,
    I was not able to filter directly using telerik's grid since I the NHibernate.Linq provider doesn't appear to be able to handle it (so I do custom filtering using manually build elements). Other than that the grid is just concerned about the IQueryable so I use Linq projections to get the best dataset possible to display the data.

  • E1tz7b Major thankies for the article post.Really looking forward to read more. Really Cool.

Comments have been disabled for this content.