ASP.NET Web API - Screencast series Part 4: Paging and Querying - Jon Galloway

ASP.NET Web API - Screencast series Part 4: Paging and Querying

We're continuing a six part series on ASP.NET Web API that accompanies the getting started screencast series. This is an introductory screencast series that walks through from File / New Project to some more advanced scenarios like Custom Validation and Authorization. The screencast videos are all short (3-5 minutes) and the sample code for the series is both available for download and browsable online. I did the screencasts, but the samples were written by the ASP.NET Web API team.

In Part 1 we looked at what ASP.NET Web API is, why you'd care, did the File / New Project thing, and did some basic HTTP testing using browser F12 developer tools.

In Part 2 we started to build up a sample that returns data from a repository in JSON format via GET methods.

In Part 3, we modified data on the server using DELETE and POST methods.

In Part 4, we'll extend on our simple querying methods form Part 2, adding in support for paging and querying.

[Video and code on the ASP.NET site]

This part shows two approaches to querying data (paging really just being a specific querying case) - you can do it yourself using parameters passed in via querystring (as well as headers, other route parameters, cookies, etc.). You're welcome to do that if you'd like.

What I think is more interesting here is that Web API actions that return IQueryable automatically support OData query syntax, making it really easy to support some common query use cases like paging and filtering. A few important things to note:

  • This is just support for OData query syntax - you're not getting back data in OData format. The screencast demonstrates this by showing the GET methods are continuing to return the same JSON they did previously. So you don't have to "buy in" to the whole OData thing, you're just able to use the query syntax if you'd like.
  • This isn't full OData query support - full OData query syntax includes a lot of operations and features - but it is a pretty good subset: filter, orderby, skip, and top.
  • All you have to do to enable this OData query syntax is return an IQueryable rather than an IEnumerable. Often, that could be as simple as using the AsQueryable() extension method on your IEnumerable.
  • Query composition support lets you layer queries intelligently. If, for instance, you had an action that showed products by category using a query in your repository, you could also support paging on top of that. The result is an expression tree that's evaluated on-demand and includes both the Web API query and the underlying query.

So with all those bullet points and big words, you'd think this would be hard to hook up. Nope, all I did was change the return type from IEnumerable<Comment> to IQueryable<Comment> and convert the Get() method's IEnumerable result using the .AsQueryable() extension method.

public IQueryable<Comment> GetComments() 
{ 
    return repository.Get().AsQueryable(); 
} 

You still need to build up the query to provide the $top and $skip on the client, but you'd need to do that regardless. Here's how that looks:

$(function () { 
    //--------------------------------------------------------- 
    // Using Queryable to page 
    //--------------------------------------------------------- 
    $("#getCommentsQueryable").click(function () { 
        viewModel.comments([]); 
 
        var pageSize = $('#pageSize').val(); 
        var pageIndex = $('#pageIndex').val(); 
 
        var url = "/api/comments?$top=" + pageSize + '&$skip=' + (pageIndex * pageSize); 
 
        $.getJSON(url, function (data) { 
            // Update the Knockout model (and thus the UI) with the comments received back  
            // from the Web API call. 
            viewModel.comments(data); 
        }); 
 
        return false; 
    }); 
});

And the neat thing is that - without any modification to our server-side code - we can modify the above jQuery call to request the comments be sorted by author:

$(function () { 
    //--------------------------------------------------------- 
    // Using Queryable to page 
    //--------------------------------------------------------- 
    $("#getCommentsQueryable").click(function () { 
        viewModel.comments([]); 
 
        var pageSize = $('#pageSize').val(); 
        var pageIndex = $('#pageIndex').val(); 
 
        var url = "/api/comments?$top=" + pageSize + '&$skip=' + (pageIndex * pageSize) + '&$orderby=Author'; 
 
        $.getJSON(url, function (data) { 
            // Update the Knockout model (and thus the UI) with the comments received back  
            // from the Web API call. 
            viewModel.comments(data); 
        }); 
 
        return false; 
    }); 
});

So if you want to make use of OData query syntax, you can. If you don't like it, you're free to hook up your filtering and paging however you think is best. Neat.

In Part 5, we'll add on support for Data Annotation based validation using an Action Filter.

Published Friday, March 16, 2012 12:27 PM by Jon Galloway
Filed under: ,

Comments

# re: ASP.NET Web API - Screencast series Part 4: Paging and Querying

Hello, Jon. Loved the post, very helpful.

Do you work with the WebAPI team?

I'd like to ask them if there's any chance of implementing at least the inlinecount odata query option.

It's vital for paging and filtering.

Thanks.

Wednesday, March 21, 2012 10:22 AM by Rafael Soares

# re: ASP.NET Web API - Screencast series Part 4: Paging and Querying

@Rafael Soares - I don't work on the Web API team. I'd recommend making that request on the ASP.NET Web API UserVoice: aspnet.uservoice.com/.../147201-asp-net-web-api

Just a guess, but I'm thinking they didn't implement that one because it specifies output behavior - $inlinecount=allpages says you want the count of all filtered items included in the results. That makes sense for an OData query that's returning XML, as there's a defined format for returning that value. Web API will support JSON, XML, and other media you provide formatters for, so there's no defined way to return that value.

Monday, March 26, 2012 12:47 PM by Jon Galloway