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
-
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 theKendo.UI.DataSourceRequestAttribute
. This attribute populates theDataSourceRequest
object.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
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() }); } } }
-
-
Create a new instance of
DataSourceResult
. Set theData
andTotal
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. }; }
-
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); }
-
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.