Inserting Transactions into Telerik’s ASP.NET MVC Grid
Default Grid Behavior Implementation
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
[Transaction]
public ActionResult List()
{
var orders = _orderRepository.Queryable; //Return IQueryable<Order>
return View(orders);
}
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
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();
}
}
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!
<%= 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.
Now that is some nice transactional SQL :)
Enjoy!