Data Services Expressions – Part 2 – The query root

Series: This post is the second part of the Data Services Expressions Series which describes expressions generated by WCF Data Services.

In this part we will first look how to watch the expressions generated by Data Services. Then we’re going to discuss our first expression, the query root.

How to see the expression?

To see the expressions generated by Data Services we need to get into the query processing pipeline. To do that, we need to implement IQueryable as that is the only public interface Data Services use to construct and execute all its queries as discussed in the Part 1.

Fortunately for me Alex just recently posted a perfect IQueryable implementation for this purpose, the InterceptedQuery<T> and InterceptingProvider. So instead of writing one from scratch, let’s just use that one.

 public IQueryable<Product> Products
{
    get
    {
        return Toolkit.InterceptingProvider.Intercept(
            products.AsQueryable(),
            (expression) => {
                Trace.WriteLine(expression.ToString()); 
                return expression; 
            });
    }
}

If you put this into your sample service, every time you issue a query against the service it will write the expression into the debug output. The simplest way to watch these is to run your service under Visual Studio debugger, the debug output will show up in the Output window.

For detailed viewing you can put a breakpoint into the above code and inspect the expression from the Watch window.

And now to our first expression.

The query root

Each query starts with a resource set. To build a query the Data Services gets the query for the resource set root and then builds on top of it. Data Services calls the IDataServiceQueryProvider.GetQueryRootForResourceSet method to get an instance of IQueryable for the given resource set. Each IQueryable has an expression which can be accessed through the IQueryable.Expression property. The expression which is in the IQueryable returned from the GetQueryRootForResourceSet is called the query root.

The query root expression is interesting to us, because we get to specify it completely. Data Services will not modify the expression, it will just use it as the starting point. The nice thing about this is that we can use even expression which Data Services would not recognize, we can use our own stuff here.

There’s only one assumption which Data Services makes about this expression and that is it’s CLR type, which is the value of Expression.Type property. This type must implement IQueryable<T> where T is the instance type of the base resource type of the resource set (ResourceSet.ResourceType.InstanceType). In the above example that would be an IQueryable<Product>.

In the sample above the query root is the “products” variable, which is an instance of List<Product>. You can see this by watching the expression generated for a query “TypedService.svc/Products”.

ProductsOutput

It looks like the query root is the List<Product> itself, but it’s not. The actual expression node is a constant expression with the value of IQueryable implementation over the list of products. To see this we’ll take a look in the debugger.

ProductsWatch

We can see that the NodeType of the expression is Constant and the value is the List<Product>. If you’re using Visual Studio 2010 and .NET 4.0 there’s a new property on each Expression called DebugView which returns more detailed text representation of the expression. In this case it returns:

 .Constant<System.Linq.EnumerableQuery`1[TypedService.Product]>
    (System.Collections.Generic.List`1[TypedService.Product])

This shows us that the Type of the expression is System.Linq.EnumerableQuery<Product> which is the LINQ to Object’s implementation of IQueryable<Product>. So it fulfills the requirement of Data Services to be of type IQueryable<Product>.

Custom query providers usually return some expression which represents the resource set root. This expression then holds information like the Table to get the data from, or the list of resources and so on.

In all our future visits to Data Services Expressions we will see this query root expression at the beginning of each query.