# Custom Binding

The Telerik UI Grid for ASP.NET MVC allows you to bypass the built-in data processing and handle operations like paging, sorting, filtering, and grouping yourself.
This article describes custom server and custom Ajax binding workflows.

The Grid provides the following custom binding options:

* [Custom Server Binding](https://www.telerik.com/aspnet-mvc/documentation/html-helpers/data-management/grid/binding/custom-binding#custom-server-binding)
* [Custom Ajax Binding](https://www.telerik.com/aspnet-mvc/documentation/html-helpers/data-management/grid/binding/custom-binding#custom-ajax-binding)

## Custom Server Binding

Use custom server binding to process Grid data operations in the controller and return prepared data to the View.

1. Add a new parameter of type `Kendo.UI.DataSourceRequest` to the action method. It must contain information about the requested Grid data operation—paging, sorting, grouping, or filtering. Decorate this parameter with the `Kendo.UI.DataSourceRequestAttribute`. This attribute populates the `DataSourceRequest` object.

   ```C#
   public ActionResult Index([DataSourceRequest(Prefix = "Grid")] DataSourceRequest request)
   {
       IQueryable<Order> orders = new NorthwindEntities().Orders;
   }
   ```

2. Assign a default `pageSize`.

   ```C#
       public ActionResult Index([DataSourceRequest(Prefix = "Grid")] DataSourceRequest request)
       {
           if (request.PageSize == 0)
           {
           request.PageSize = 10;
           }

           IQueryable<Order> orders = new NorthwindEntities().Orders;
       }
   ```

3. Handle the respective data operations.

   ```C#
   public ActionResult Index([DataSourceRequest(Prefix = "Grid")] DataSourceRequest request)
   {
       if (request.PageSize == 0)
       {
           request.PageSize = 10;
       }
       IQueryable<Order> orders = new NorthwindEntities().Orders;

       if (request.Sorts.Any())
       {
           foreach (SortDescriptor sortDescriptor in request.Sorts)
           {
               if (sortDescriptor.SortDirection == ListSortDirection.Ascending)
               {
                   switch (sortDescriptor.Member)
                   {
                       case "OrderID":
                           orders= orders.OrderBy(order => order.OrderID);
                           break;
                       case "ShipAddress":
                           orders= orders.OrderBy(order => order.ShipAddress);
                           break;
                   }
               }
               else
               {
                   switch (sortDescriptor.Member)
                   {
                       case "OrderID":
                           orders= orders.OrderByDescending(order => order.OrderID);
                           break;
                       case "ShipAddress":
                           orders= orders.OrderByDescending(order => order.ShipAddress);
                           break;
                   }
               }
           }
       }
       else
       {
           // If cannot page unsorted data.
           orders = orders.OrderBy(o => o.OrderID);
       }

       // Apply paging.
       if (request.Page > 0) {
           orders = orders.Skip((request.Page - 1) * request.PageSize);
       }
       orders = orders.Take(request.PageSize);
   }
   ```

4. Calculate the total number of records.

   ```C#
   public ActionResult Index([DataSourceRequest(Prefix = "Grid")] DataSourceRequest request)
   {
       if (request.PageSize == 0)
       {
           request.PageSize = 10;
       }
       IQueryable<Order> orders = new NorthwindEntities().Orders;

       // Apply sorting (code omitted).
       // Calculate the total number of records before paging.
       var total = orders.Count();
       // Apply paging.

       ViewData["total"] = total;
   }
   ```

5. Pass the processed data to the View.

   ```C#
   public ActionResult Index([DataSourceRequest]DataSourceRequest request)
   {
       // Get the data (code omitted).
       // Apply sorting (code omitted).
       // Calculate the total number of records (code omitted).
       // Apply paging (code omitted).

       return View(orders);
   }
   ```

6. Set `EnableCustomBinding(true)` through the Grid widget declaration.

   ```Razor HtmlHelper
   @model IEnumerable<KendoGridCustomServerBinding.Models.Order>

   @(Html.Kendo().Grid(Model)
       .Name("Grid")
       .EnableCustomBinding(true)
       .Columns(columns => {
           columns.Bound(o => o.OrderID);
           columns.Bound(o => o.ShipAddress);
       })
       .Pageable()
       .Sortable()
       .Scrollable()
   )
   ```

7. If paging is enabled, assign the total number of records through the `DataSource`.

   ```Razor HtmlHelper
       @model IEnumerable<KendoGridCustomServerBinding.Models.Order>

       @(Html.Kendo().Grid(Model)
           .Name("Grid")
           .EnableCustomBinding(true)
           .Columns(columns => {
               columns.Bound(o => o.OrderID);
               columns.Bound(o => o.ShipAddress);
           })
           .Pageable()
           .Sortable()
           .Scrollable()
           .DataSource(dataSource => dataSource
               .Server()
               .Total((int)ViewData["total"]) // set the total number of records
           )
       )
   ```

To download the Visual Studio project, refer to the [Grid custom server binding sample in the Telerik UI for ASP.NET MVC examples repository](https://github.com/telerik/ui-for-aspnet-mvc-examples/tree/master/Telerik.Examples.Mvc/Telerik.Examples.Mvc/Areas/GridCustomServerBinding).

For a complete example, refer to the [demo on implementing server binding of the Grid](https://demos.telerik.com/aspnet-mvc/grid/serverbinding).

## Custom Ajax Binding

Use custom Ajax binding to process Grid operations on the server and return a JSON response.

1. Add a new parameter of type `Kendo.UI.DataSourceRequest` to the action method. It must contain information about the requested Grid data operation—paging, sorting, grouping, or filtering. Decorate this parameter with the `Kendo.UI.DataSourceRequestAttribute`. This attribute populates the `DataSourceRequest` object.

   ```C#
   public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
   {
       IQueryable<Order> orders = new NorthwindEntities().Orders;
   }
   ```

2. Handle the respective data operations and calculate the total number of records:

   * Filtering

     The following code applies the filter descriptors from the Grid request to the orders query.

     ```C#
     namespace ProjectName.Controllers
     {
         public class GridController : Controller
         {
             public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
             {
                 IQueryable<Order> orders = new NorthwindEntities().Orders;
                 orders = orders.ApplyOrdersFiltering(request.Filters); // Pass the filter expressions from the request object.
                 var total = orders.Count();
             }
         }

         public static class AjaxCustomBindingExtensions
         {
             public static IQueryable<Order> ApplyOrdersFiltering(this IQueryable<Order> data, IList<IFilterDescriptor> filterDescriptors)
             {
                 if (filterDescriptors.Any())
                 {
                     data = data.Where(ExpressionBuilder.Expression<Order>(filterDescriptors, false));
                 }
                 return data;
             }
         }
     }
     ```

   * Sorting

     The following code applies the sort descriptors from the Grid request to the orders query.

     ```C#
     namespace ProjectName.Controllers
     {
         public class GridController : Controller
         {
             public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
             {
                 IQueryable<Order> orders = new NorthwindEntities().Orders;

                 ... // Filtering (code omitted).
                 var total = orders.Count();

                 orders = orders.ApplyOrdersSorting(request.Groups, request.Sorts); // Pass the sort expressions and groups from the request object.

                 if (!request.Sorts.Any() && !request.Groups.Any() && !request.Groups.Where(descriptor=>descriptor.SortDirection != ListSortDirection.Descending).Any())
                 {
                     // Entity Framework does not support paging on unsorted data.
                     orders = orders.OrderBy(o => o.OrderID);
                 }
             }
         }

         public static class AjaxCustomBindingExtensions
         {
             public static IQueryable<Order> ApplyOrdersSorting(this IQueryable<Order> data, IList<GroupDescriptor> groupDescriptors, IList<SortDescriptor> sortDescriptors)
             {
                 if (groupDescriptors != null && groupDescriptors.Any())
                 {
                     foreach (var groupDescriptor in groupDescriptors.Reverse())
                     {
                         data = AddSortExpression(data, groupDescriptor.SortDirection, groupDescriptor.Member);
                     }
                 }

                 if (sortDescriptors != null && sortDescriptors.Any())
                 {
                     foreach (SortDescriptor sortDescriptor in sortDescriptors)
                     {
                         data = AddSortExpression(data, sortDescriptor.SortDirection, sortDescriptor.Member);
                     }
                 }

                 return data;
             }

             private static IQueryable<Order> AddSortExpression(IQueryable<Order> data, ListSortDirection
                         sortDirection, string memberName)
             {
                 if (sortDirection == ListSortDirection.Ascending)
                 {
                     switch (memberName)
                     {
                         case "OrderID":
                             data = data.OrderBy(order => order.OrderID);
                             break;
                         case "ShipAddress":
                             data = data.OrderBy(order => order.ShipAddress);
                             break;
                     }
                 }
                 else
                 {
                     switch (memberName)
                     {
                         case "OrderID":
                             data = data.OrderByDescending(order => order.OrderID);
                             break;
                         case "ShipAddress":
                             data = data.OrderByDescending(order => order.ShipAddress);
                             break;
                     }
                 }
                 return data;
             }
         }
     }
     ```

   * Paging

     The following code applies the requested page number and page size to the orders query.

     ```C#
     namespace ProjectName.Controllers
     {
         public class GridController : Controller
         {
             public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
             {
                 IQueryable<Order> orders = new NorthwindEntities().Orders;

                 ... // Filtering (code omitted).
                 var total = orders.Count();

                 ... // Sorting (code omitted).

                 orders = orders.ApplyOrdersPaging(request.Page, request.PageSize);

             }
         }

         public static class AjaxCustomBindingExtensions
         {
             public static IQueryable<Order> ApplyOrdersPaging(this IQueryable<Order> data, int page, int pageSize)
             {
                 if (pageSize > 0 && page > 0)
                 {
                     data = data.Skip((page - 1) * pageSize);
                 }
                 data = data.Take(pageSize);
                 return data;
             }
         }
     }
     ```

   * Grouping

     The following code applies the group descriptors from the Grid request to the orders query.

     ```C#
     namespace ProjectName.Controllers
     {
         public class GridController : Controller
         {
             public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
             {
                 IQueryable<Order> orders = new NorthwindEntities().Orders;

                 ... // Filtering (code omitted).
                 var total = orders.Count();

                 ... // Sorting (code omitted).

                 ... // Paging (code omitted).

                 IEnumerable data = orders.ApplyOrdersGrouping(request.Groups);
             }
         }

         public static class AjaxCustomBindingExtensions
         {
             public static IEnumerable ApplyOrdersGrouping(this IQueryable<Order> data, IList<GroupDescriptor>
                 groupDescriptors)
             {
                 if (groupDescriptors != null && groupDescriptors.Any())
                 {
                     Func<IEnumerable<Order>, IEnumerable<AggregateFunctionsGroup>> selector = null;
                     foreach (var group in groupDescriptors.Reverse())
                     {
                         if (selector == null)
                         {
                             if (group.Member == "OrderID")
                             {
                                 selector = Orders => BuildInnerGroup(Orders, o => o.OrderID);
                             }
                             else if (group.Member == "ShipAddress")
                             {
                                 selector = Orders => BuildInnerGroup(Orders, o => o.ShipAddress);
                             } 
                         }
                         else
                         {
                             if (group.Member == "OrderID")
                             {
                                 selector = BuildGroup(o => o.OrderID, selector);
                             }
                             else if (group.Member == "ShipAddress")
                             {
                                 selector = BuildGroup(o => o.ShipAddress, selector);
                             }
                         }
                     }
                     return selector.Invoke(data).ToList();
                 }
                 return data.ToList();
             }

             private static Func<IEnumerable<Order>, IEnumerable<AggregateFunctionsGroup>>
                 BuildGroup<T>(Expression<Func<Order, T>> groupSelector, Func<IEnumerable<Order>,
                 IEnumerable<AggregateFunctionsGroup>> selectorBuilder)
             {
                 var tempSelector = selectorBuilder;
                 return g => g.GroupBy(groupSelector.Compile())
                             .Select(c => new AggregateFunctionsGroup
                             {
                                 Key = c.Key,
                                 HasSubgroups = true,
                                 Member = groupSelector.MemberWithoutInstance(),
                                 Items = tempSelector.Invoke(c).ToList()
                             });
             }

             private static IEnumerable<AggregateFunctionsGroup> BuildInnerGroup<T>(IEnumerable<Order>
                 group, Expression<Func<Order, T>> groupSelector)
             {
                 return group.GroupBy(groupSelector.Compile())
                         .Select(i => new AggregateFunctionsGroup
                         {
                             Key = i.Key,
                             Member = groupSelector.MemberWithoutInstance(),
                             Items = i.ToList()
                         });
             }
         }
     }
     ```

3. Create a new instance of `DataSourceResult`. Set the `Data` and `Total` properties to the processed data and to the total number of records.

   ```C#
   public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
   {
       IQueryable<Order> orders = new NorthwindEntities().Orders;

       ... // Filtering (code omitted).
       var total = orders.Count();

       ... // Sorting, Paging and Grouping (code omitted).

       // Initialize the DataSourceResult.
       var result = new DataSourceResult()
       {
           Data = orders, // Process data (filtered, sorted, paged, grouped data).
           Total = total // The total number of records.
       };
   }
   ```

4. Return the `DataSourceResult` as JSON.

   ```C#
   public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request)
   {
       IQueryable<Order> orders = new NorthwindEntities().Orders;
       ... // Filtering (code omitted).
       var total = orders.Count();

       ... // Sorting, Paging and Grouping (code omitted).
       
       var result = new DataSourceResult()
       {
           Data = orders,
           Total = total
       };

       // Return the result as JSON.
       return Json(result);
   }
   ```

5. Configure the Grid for custom Ajax binding.

   ```Razor HtmlHelper
       @(Html.Kendo().Grid<KendoGridCustomAjaxBinding.Models.Order>()
           .Name("grid")
           .Columns(columns => {
               columns.Bound(o => o.OrderID);
               columns.Bound(o => o.ShipAddress);
           })
           .Pageable()
           .Sortable()
           .Filterable()
           .Groupable()
           .Scrollable()
           .DataSource(dataSource => dataSource
               .Ajax()
               .PageSize(15)
               .Read("Orders_Read", "Home")
           )
       )
   ```

To download the Visual Studio project, refer to the [Grid custom Ajax binding sample in the Telerik UI for ASP.NET MVC examples repository](https://github.com/telerik/ui-for-aspnet-mvc-examples/tree/master/Telerik.Examples.Mvc/Telerik.Examples.Mvc/Areas/GridCustomAjaxBinding).

For a complete example, refer to the [demo on implementing custom Ajax binding of the Grid](https://demos.telerik.com/aspnet-mvc/grid/customajaxbinding).

## See Also

* [Custom Server Binding by the Grid HtmlHelper for ASP.NET MVC (Demo)](https://demos.telerik.com/aspnet-mvc/grid/customserverbinding)

* [Custom Ajax Binding by the Grid HtmlHelper for ASP.NET MVC (Demo)](https://demos.telerik.com/aspnet-mvc/grid/customajaxbinding)

* [Server-Side API](https://www.telerik.com/aspnet-mvc/documentation/api/grid)
