Data Services Expressions – Part 5 – Sorting
Series: This post is the fifth part of the Data Services Expressions Series which describes expressions generated by WCF Data Services.
This time we will look at how WCF Data Services translates sorting expressions. Sorting expression are used when the URL uses a query option $orderby.
Simple sorting
In the expression trees, sorting is expressed by a call to the OrderBy method. In the case of WCF Data Services, since the query is expressed as IQueryable, the exact method is System.Linq.Queryable.OrderBy extension method. First parameter of this method is the implicit IQueryable and the second parameter is a lambda expression which returns the value according to which the results should be sorted.
Let’s take a sample query like:
https://host/service.svc/Products?$orderby=Price
WCF Data Services will translate this into an expression like:
System.Collections.Generic.List`1[TypedService.Product]
.OrderBy(element => element.Price)
So it’s a query root (see this blog post for details) followed by a call to the OrderBy with a lambda which takes the product and extracts its Price property. Note that for sorting expressions WCF Data Services uses the name “element” as the name of parameter expression for the lambda expression.
The lambda body in this case is a simple property access and as such it will follow all the rules described in my previous post about accessing properties.
More complex sorting expression
That was a really simple sample, but in reality it usually gets more complicated. WCF Data Services allows usage of expressions in the $orderby query options. So we can use for example arithmetic like in this query:
https://host/services.svc/Products?$orderby=5 sub Rating
The expression generated looks like this:
System.Collections.Generic.List`1[TypedService.Product]
.OrderBy(element => (5 - element.Rating))
It can obviously get even more complicated, but the expression tree generated would just follow along, without any real surprises.
Multiple sorting expressions
If we want to specify that the results should be sorted according to multiple sorting expressions, in the query we add more expressions into the $orderby query option and separate them using comma characters. So for example:
https://host/services.svc/Products?$orderby=Rating,Price
Unfortunately the OrderBy method which is called in the expression tree to perform the sorting only takes one lambda expression. So to express more sorting expressions another method called ThenBy is used. As with OrderBy the ThenBy in our case is in fact System.Linq.Queryable.ThenBy extension method and it looks exactly the same as OrderBy.
The expression tree for the above query will look like:
System.Collections.Generic.List`1[TypedService.Product]
.OrderBy(element => element.Rating)
.ThenBy(element => element.Price)
Note that if there’s more sorting expressions, other ThenBy calls would be simple appended to the expression tree here. As a result a single $orderby query option will be translated as a call to OrderBy method followed by possibly multiple calls to ThenBy method.
Ascending or Descending
WCF Data Services also allows us to specify the sorting direction. By default sorting is done in the ascending order (that is 1, 2, 3, …), we can change that to the descending order by adding the keyword “desc” to the sorting expression:
https://host/services.svc/Products?$orderby=Rating desc
In the expression tree the change of sorting direction is expressed by calling yet another method OrderByDescending. Other than its name everything else about this method is the same as for the OrderBy method. So the expression tree looks like:
System.Collections.Generic.List`1[TypedService.Product]
.OrderByDescending(element => element.Rating)
There’s also a ThenByDescending which is used when more sorting options are specified some of which use the descending order. Again ThenByDescending is exactly the same as ThenBy, except for its name.
And to show it all of, let’s take our ultimate sorting example:
https://host/services.svc/Products?$orderby=Rating desc,Price,ReleaseDate desc
The expression tree generated is this:
System.Collections.Generic.List`1[TypedService.Product]
.OrderByDescending(element => element.Rating)
.ThenBy(element => element.Price)
.ThenByDescending(element => element.ReleaseDate)
The last note for this blog post is about the OrderBy, ThenBy, OrderByDescending and ThenByDescending methods. These methods are defined as returning an IOrderedQueryable interface and so if you’re writing your own query provider be sure to support it correctly as described here.