It has been more than a year since the last time we discussed WebAPI with WebForms, and a lot has changed in that time. Microsoft has released a 1.0 release of WebAPI and Telerik now supports using WebAPI as a client-side datasource for many of our controls. In this article, I will show you how to configure WebAPI to work with one of Telerik’s most popular controls, our Grid for ASP.NET AJAX. Without changing the appearance of the grid, I will convert the grid from server-side databinding to client-side databinding and show you how it supports sorting, filtering and paging with WebAPI.
There are several ways to configure WebAPI for OData use, as it is extremely flexible in its implementation. If you are using Telerik OpenAccess with your project, you can use a built in wizard to construct an appropriate WebAPI for your domain model. Otherwise, we can build one by hand with syntax that mimics that of an ASP.Net MVC controller. I built a simple WebAPI for our ConferenceBuddy application in a prior post, and I am going to use that base API for this sample.
In that original API, we derived our API from ApiController:
EventsController : ApiController
This configures WebAPI to interact and respond with a set of POCOs (Plain Old CLR Objects). To allow WebAPI to interact with OData queries and return data properly shaped like an OData resultset, we have three choices with varying degrees of complexity and return on our coding investment.
With the help of a NuGet package called Microsoft.AspNet.WebApi.OData we can decorate our ApiController or its methods with a Queryable attribute and they will be able to handle OData query commands. I can modify the EventsController’s Get method signature to look like the following:
Our Get method can now handle requests like:
Data will be returned as a collection of events, serialized in JSON, XML, or whatever format you may have configured your HTTP Request to accept.
The drawback of this approach is that it is not a complete implementation of OData query, and you are not going to receive information about the size of the resultset delivered. The only OData query arguments supported by this implementation are $filter, $orderby, $skip, and $top. A significant piece that is missing from this collection is the interpretation of the $inlinecount query argument. As the data that is returned in our sample is a collection of Events, there is no logical place to deposit the count in the resultset.
This partial implementation will do funny things to your AJAX controls that you try to bind to the WebAPI service. If you attempt to use paging or any other operation that needs access to the total recordcount, it will not report correct information and the control’s pager will not function properly.
The next option in our list is to handle the OData query and return appropriately formatted data ourselves. This is the “manual” approach to ensuring that data is accepted and returned properly. To implement this option, we will need to change the signature of our Get method to accept a collection of ODataQueryOptions and return an OData PageResult:
PageResult<Event> Get(ODataQueryOptions<Event> options)
With this signature change in play, you will need to change how your data is returned as it can no longer be formatted as an IQueryable. To convert it to a PageResult and apply the OData query, consider the following modification:
var ctx =
var events = ctx.Events.AsQueryable();
var results = options.ApplyTo(events);
IEnumerable<Event>, Request.GetNextPageLink(), Request.GetInlineCount());
On the last few lines, I have applied the ODataQueryOptions to the IQueryable. This applies those query arguments that were submitted and returns another IQueryable. To format that data as a PageResult, we pass in the IQueryable and generate NextPage links and the count of the elements being returned. The resulting data is in a different shape than we originally saw, with output looking more like a standard OData output:
The clean most compliant way to implement OData query access is to use an ODataController instead of an ApiController in your WebAPI implementation. There is a small amount of pre-configuration required prior to working with this type of controller. From the routeconfig.cs file, I need to add some information about the data types that OData will be handling and add the appropriate route information:
var modelBuilder =
var model = modelBuilder.GetEdmModel();
, model: model);
By defining the model and route, our ODataController can now have the following syntax for the Get method:
EventController : ODataController
… and will render JSON requests in this format:
Note that the CountPropertyName and the DataPropertyName are configured for second option of the WebAPI configuration I previously defined. These attributes tell the control where to find the data in the API resultset.
With these few changes in play, we can see our grid looks the same with a working pager, filter, and sorting all operating on the client side.
Fiddler verifies this configuration for us, by demonstrating that our query went through and data was returned in JSON format:
In this article, I showed you how to configure a WebAPI controller to allow interaction as an OData endpoint. We then made a very small set of changes to a previously implemented RadGrid to allow rich and simple interactions with the server for only the data we need to re-populate the grid. This slimmer interaction with the server will give you much better perceived performance for those with accessing your application with a low bandwidth connection.
Jeffrey T. Fritz is a Microsoft MVP in ASP.Net and an ASPInsider with more than a decade of experience writing and delivering large scale multi-tenant web applications. After building applications with ASP, ASP.NET and now ASP.NET MVC, he is crazy about building web sites for all sizes on any device. You can read more from Jeffrey on his personal blog or on Twitter at @csharpfritz. Google Profile CodeProject
Copyright © 2017, Progress Software Corporation and/or its subsidiaries or affiliates. All Rights Reserved.
Progress, Telerik, and certain product names used herein are trademarks or registered trademarks of Progress Software Corporation and/or one of its subsidiaries or affiliates in the U.S. and/or other countries. See Trademarks or appropriate markings.