Saturday, 20 October 2012

How ASP.NET 4.5 Model Binding and IQueryable work behind the scenes

Model Binding is a nice new feature added to ASP.NET 4.5. Using this feature, we can bind data to controls in a type-safe way. This means, the properties of the data are known at the compile time.

If a method returning IQueryable type of collection is bound to a GridView, paging and sorting can be enabled by applying some simple configurations. There is no need of writing any extra amount of code to handle paging or sorting.

But, the question here is, how does that work? Is it a magic? The answer is very simple: dynamic nature of IQueryable. IQueryable operators can be invoked dynamically.

Following is signature the Where operator of IQueryable:
public static IQueryable<TSource> Where<TSource>(this IQueryable<TSource> source, Expression<Func<TSource, bool>> predicate);
Second parameter of the operator is of Expression<Func> type. The difference between a Func and an Expression is Func is a delegate whereas Expression is a data structure. This means, the contents of an Expression can be parsed and inspected. This also means that, if we have every piece of information that an Expression needs, we can build it dynamically. Have a look at the inputs that the other IQueryable operators take, they all accept Expression types.

For instance, say following is the GridView used on an ASP.NET web form:
<asp:GridView runat="server" AutoGenerateColumns="false" ID="gvCustomers" 
        ItemType="WhatsNewInAspNet45.DataAccess.Customer" DataKeyNames="CustomerID" SelectMethod="GetCustomers" AllowPaging="true" PageSize="10"         AllowSorting="true">
        <Columns>
            <asp:TemplateField HeaderText="Company Name" SortExpression="CompanyName">
                <ItemTemplate>
                    <%#: Item.CompanyName %>
                </ItemTemplate>
            </asp:TemplateField>
      ...
      ...
</asp:GridView>
Following is a simple implementation of the GetCustomers method:
public IQueryable<Customer> GetCustomers()
{
    return _Context.Customers;
}
When the page containing this GridView is viewed on a browser, the user interacting with the page can sort the data based on data in a column and also can navigate through different pages of the GridView. When we click on a column of the GridView to sort the data, SortExpression of the column will be picked up and it will be used in the OrderBy operator of IQueryable to fetch data. As GetCustomers returns IQueryable type of data, we may say that, result of the following statement is used to bind data to the GridView:
GetCustomers().OrderBy(c=>c.CompanyName);

Digging a bit more, we may say that, the following LINQ query is evaluated:
_Context.Customers.OrderBy(c=>c.CompanyName);

If the SelectMethod GetCustomers uses Entity Framework or LinqToSQL, the SQL query that will be formed to hit the database because of the above LINQ query would contain an order by clause. Otherwise, if the data is fetched from a WCF Data Service, the URL formed to invoke the service would contain an order by expression.

Reason behind this behaviour is deferred execution of LINQ queries. They get executed when they are needed. We require data before it is bound to a user interface control. Till then, the query will not be parsed.

Similarly, if we navigate to any page on the GridView, Skip and Take operators are applied on the result of select method to fetch the data that should appear on the target page. If SelectMethod returns any other type of data (i.e., other than IQueryable), we need to write logic for fetching appropriate data and returning it to the target control.

Happy coding!

1 comment: