Supporting OData $inlinecount with the new Web API OData preview package

OData support in Web API is arguably one of its hottest features. However, it’s support in Web API has been a bumpy ride – initially, OData was supported in a limited way only, and ultimately ended up being yanked altogether from the Web API RTM. It is however stil lpossible to use OData with Web API, only in a slighly different form , as an external NuGet package, which, in its pre-release alpha format was published last Wednesday, along the Web API RTM release.

This package is called Microsoft ASP.NET Web API OData and is a joint effort by Microsoft’s Web API and OData teams. Alex James has written a great introduction to the package, so I recommend reading it.

In the meantime, let me show you how to add $inlinecount support as for the time being, it’s still not provided there out of the box.

What’s supported currently?

If you use the OData package, you get the QueryableAttribute, with which oyu should be familiar from earlier Web API releases. It works pretty much as it used to (however there are some breaking changes, i.e. the lack of ApplyResultsLimit method) and gives you support for $filter, $orderby, $skip and $top OData queries.

So, no $inlinecount. Therefore, let’s tackle the problem.

What is $inlinecount anyway?

If you need a good definition, odata.org is a great resource for OData related information. In this case it explains $inlinecount accordingly: “URI with a $inlinecount System Query Option specifies that the response to the request includes a count of the number of Entries in the Collection of Entries identified by the Resource Path section of the URI. The count must be calculated after applying any $filter System Query Options present in the URI.”

The possible options for $inlinecount are:
allpages – equivalent to asking “please show me the count of items”
none – this is equivalent to not having inlinecount in the query at all

While normal API responsewould look something like this:

Response that includes $inlinecount would look:

Which indicates the caller, that you got one “blog” object result, but there are actually 3 total in store.

Adding a simple wrapper model

In order to support inlinecount we need a wrapper model, that would wrap our IEnumerable results and expose two properties: Count and Results.

It’s very simple:

Adding the action

The action will need to return System.Web.Http.OData.ODataResult<T>, not a regular IQueryable or IEnumerable. This will allow us to package the “count” of items with it and flush it down the pipeline.

However, we will then have to intercept this object and assign it to our wrapper model. If you ask why we don’t return that custom wrapper format directly – we have to use ODataResult as return Type, as otherwise the model binder would complain about not being able to bind ODataQueryOptions:

Category=’System.Web.Http.ModelBinding’, Id=e93b9f25-4c5c-4324-8294-068a004fa597, Message=’UserMessage=’The server failed to retrieve a inner generic type from type ‘webapi_metadata.Models.ODataMetadata`1[[webapi_metadata.Models.Blog, webapi-metadata, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null]]’ against action ‘GetBlogs’ on controller ‘Blog’.”, Operation=ODataQueryParameterBinding.ExecuteBindingAsync, Status=500 (InternalServerError)

And by accepting ODataQueryOptions into the action, we get access to all OData parameters passed by the user.

Now, this action is pretty ugly, but that’s what you need to do to process the OData query options properly. Of course this could easily be abstracted away to a separate method to provide clean, reusable code, but that’s really not the scope of this post.

So first, we grab the IQueryable or IEnumerable collection of our data from our repository. Then we go ahead and manually apply all OData queries, such as filter, orderby, skip, top.

Notice that we count the items, for the purpose of our $inlinecount, after applying $filter. The reason for that is, as mentioned in the definition before, OData spec defines that inlinecount should be calculated after filter, but before other queries.

We return the object from the action as ODataResult<Blog> – and we pass to it the result collection, next page link (null) and the inlinecount.

Now, you might think that it’s the end, but it’s not, because ODataResult<T> is IEnumerable itself which means when it is passed to the client it will arrive only as the collection of results, and there will be no indication of count.

Hijacking ODataResult<T> with a message handler

The easiest thing to do now, is to intercept the response and reassign it from ODataResult to our custom ODataMetadata.

In this case, what we do is:
1) Check if the response content object is ODataResult – if not, do not process
2) We cast it twice, once as IEnumerable – to get access to the results collection
3) The second cast is to ODataResult which is an asbtract class exposing only the count
4) Finally, we use the result collection and the inlinecount to construct the ODataMetaData and return it as a response. Notice that I check for the count being null – if you recall fro mthe controller’s action, if the count is null it means the $inlinecount was not requested by the user. This means, we don’t need to modify the response object at all, and can continue flushing down the default ODataResult>T< to the client.

Trying it out

1. Normal – no inlinecount – /api/blog/

2. With $inlinecount & $top – /api/blog?$top=2&$inlinecount=allpages

3. With $inlinecount & $top & $skip- /api/blog?$top=2&$skip=1&$inlinecount=allpages

4. With $filter & $inlinecount – /api/blog?$filter=BlogId gt 1&$inlinecount=allpages
In this case “count” refers to results after filter has been applied.

Summary

This may not be the prettiest solution in the world, and it’s a little hacky, but I hope it will point you in the right direction. Web API OData package is going to support this out of the box (via QueryableAttribute) at some point anyway, but it’s always interesting to hack away at things in the meantime πŸ™‚

This is also a good replacement technique for the missing ApplyResultsLimit override of the QueryableAttribute which was the focal point of my previous post on the OData in Web API. That solution no longer works with Web API RTM, due to the mentioned removal of “ApplyResultsLimit”, but you could reimplement that entire solution using the approach shown here.

Update – 6h after this has been originally posted πŸ™‚

Following a suggestion from Alex James, let me mention a slightly different way of implementing this.

Your action will now look like any typical action:

Notice how clean it is, and that it is not concerned about any of the OData extravaganza.

Now, instead of a handler, you can use a custom version of a QueryableAttribute.

So what’s happening there:
1. We grab $inlinecount from the request querystring, and if it’s equal to allpages, it means that the client wants to have the count – so we count all the items in the collection
2. We let the OData QueryableAttribute do its thing and apply all the queries. Notice that, I overrode ValidateQuery, otherwise the current version of the QueryableAttribute will thrown an exception when it sees $inlinecount.
3. After the OData queries are applied, we can continue morphing the response to what we want. If the $inlinecount was requested by the caller, we return our custom ODataMetadata object, if not, we countinue just returning the collection.

Now, if you opt for this approach you lose some, win some.

The pluses:
– your action is back to being very clean and you can again return your POCO from it
– you can use an attribute instead of handler (probably more readable, and doesn’t run for all requests)

The minuses:
– the $inlinecount now always refers to your entire collection prior to applying OData query, which means the $inlinecount will be calculated after $filter and before other, but rather before everything
– you need to implement your own validation check against disallowed OData queries