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. This article describes custom server and custom Ajax binding workflows.
Custom Ajax Binding
Use custom Ajax binding to process Grid operations on the server and return a JSON response.
-
Add a new parameter of type
Kendo.UI.DataSourceRequestto the action method. It must contain information about the requested Grid data operation—paging, sorting, grouping, or filtering. Decorate this parameter with theKendo.UI.DataSourceRequestAttribute. This attribute populates theDataSourceRequestobject.C#public ActionResult Orders_Read([DataSourceRequest]DataSourceRequest request) { IQueryable<Order> orders = new NorthwindEntities().Orders; } -
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() }); } } }
-
-
Create a new instance of
DataSourceResult. Set theDataandTotalproperties 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. }; } -
Return the
DataSourceResultas 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); } -
Configure the Grid for custom Ajax binding.
Razor@(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") ) )
For a complete example, refer to the demo on implementing custom Ajax binding of the Grid.