New to Telerik UI for ASP.NET Core? Download free 30-day trial

Custom Binding

The Telerik UI Grid for ASP.NET Core allows you to bypass the built-in data processing and handle operations like paging, sorting, filtering, and grouping yourself.

Custom Ajax Binding

  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.

    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

      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

      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

      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

      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.

    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.

    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.

        @(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")
            )
        )
    
        @addTagHelper *, Kendo.Mvc
    
        <kendo-grid name="grid">
            <datasource type="DataSourceTagHelperType.Ajax" page-size="15">
                <transport>
                    <read url="@Url.Action("Orders_Read", "Home")"/>
                </transport>
            </datasource>
            <columns>
                <column field="OrderID"/>
                <column field="ShipAddress"/>
            </columns>
            <pageable enabled="true"/>
            <sortable enabled="true"/>
            <filterable enabled="true"/>
            <scrollable enabled="true"/>
            <groupable enabled="true"/>
        </kendo-grid>
    

For a complete example, refer to the demo on implementing custom Ajax binding of the Grid.

See Also

In this article