WCF – Queryable over SOAP using OData URI’s

September 3, 2013

tags: ,
no comments

OData protocol offers a rich and powerful query API over the data exposed from the data service.
Usually, the query description is aggregated to the URI of the service operation as the query string, then the service can translate that query back on to the queryable model and return the specific query desired by the client.

If you used it before, you must have seen the power it brings you in exposing the data to many clients and platforms to support each and every need.

In addition to WCF Data Services, ASP.NET WebAPI has too adopted this powerful approach. You can simply apply a [Queryable] attribute on your WebAPI service operation and expose the queryable model with support to this rich OData query API.

I understand the concept of OData which aims to serve and manage data over a standard and interoperable protocol, however I found several projects which revolves around building SOAP services with WS-* standards that wished to have that too.
For example, I could have a distributed .NET system with WS-* as well as binary encoding, but I simply want to support that powerful query API, not as a goal for interoperability, but as a goal of allowing the consumer to define the query that it wishes.

Yes, I could implement a generic a way for the client to provide the different parameters (‘top’, ‘skip’, ‘orderby’, etc) and apply it in the service, but that’s exactly what the libraries around OData provide, so naturally I wanted to build over that.

Basically, I needed a provider to build the standard URI from an expression on the client side and a way to translate it back on the queryable model on the other end.
Unfortunately, the implementation in the WCF Data Services remains private, and the ODataLib family seems to rely heavily on the http stack.
Googling a bit further, I found this nifty little project Linq2Rest which provided me with what I needed.

So.. how does it look like?

Service Side

Similar to ASP.NET WebAPI, your service needs to expose the queryable model and decorate it with a simple attribute as follows –

Code Snippet
  1. [ServiceContract]
  2. interface IFooService
  3. {
  4.     [OperationContract]
  5.     IQueryable<Foo> GetFoos();
  6. }
  7.  
  8. class FooService : IFooService
  9. {
  10.     private static readonly IQueryable<Foo> FoosContext =
  11.         Enumerable.Range(0, 100).Select(i => new Foo(i)).ToArray().AsQueryable();
  12.  
  13.     [Queryable(MaxResults = 2)]
  14.     public IQueryable<Foo> GetFoos()
  15.     {
  16.         return FoosContext;
  17.     }
  18. }

That’s it, you’re done! Now you support OData standard URI requests on your queryable model.

As shown in the example, you can define the ‘MaxResults’ to ensure the maximum capacity of the result.

One last thing, you can use ‘IEnumerable<>’ as your return type if that is what you prefer in your case.

Another small feature – applying the query within the operation:

In some cases the need of applying the query request within the service operation might surface because you might want to do something with the end result before exiting.

If you encounter such a case, I prepared a method you can you use –

Code Snippet
  1. [Queryable(MaxResults = 2)]
  2. public IQueryable<Foo> GetFoos()
  3. {
  4.     //If you need to apply the query before returning the result
  5.     //because you might want to do something with the end result, you can
  6.     var appliedQuery = FoosContext.TryApplyQueryableGet();
  7.     //..do something with the applied query
  8.     return appliedQuery;
  9. }

 

Client Side

Normally, when you wish to call the service, you would create a channel and call the ‘GetFoos’ method.
I built some infrastructure on the client side to enable you to create query against this operation.

Synchronous Invocation

Code Snippet
  1. private static FooServiceClient CreateChannel()
  2. {
  3.     return new FooServiceClient();
  4. }
  5.  
  6. private static void SyncCall()
  7. {
  8.     var channel = CreateChannel();
  9.  
  10.     var syncGet = QueryableGet.CreateSync(channel, channel.GetFoos);
  11.  
  12.     var foos = syncGet.Query
  13.             .Where(f => f.Id > 3 && f.Id < 10)
  14.             .Skip(1)
  15.             .Take(3)
  16.             .OrderByDescending(f => f.Name);
  17.  
  18.     foreach (var foo in foos)
  19.     {
  20.         Console.WriteLine(foo.Name);
  21.     }
  22.  
  23.     channel.Close();
  24. }

As you can see, you construct the channel as you normally would. Then, you can create a ‘QueryableGet’ from this channel and the operation you would like to use. Finally, it provides you with an IQueryable of that model which you can use to define your query and just enumerate over the results.

Asynchronous Invocation

The support for asynchronous invocation relies on the fact that you have asynchronous operations defined on the service contract.
Once you do, you create the QueryableGet to map against the asynchronous operation, and that gives you a method that you can use to define your query and receive the task-based asynchronous operation that returns the results.

Both APM (Begin/End) and TPM (Task-based) are supported.

Code Snippet
  1. private static async Task AsyncCall()
  2. {
  3.     var channel = CreateChannel();
  4.  
  5.     //We need to cast the result because the generated proxy uses 'Foo[]' as the result
  6.     var asyncGet = QueryableGet.CreateAsync(channel,
  7.         () => channel.GetFoosAsync().CastResult<Foo[], IEnumerable<Foo>>());
  8.  
  9.     //if we had begin/end, we could do the following –
  10.     //var asyncGet = QueryableGet.CreateAsync(channel,
  11.     //    () => Task<IEnumerable<Foo>>.Factory.FromAsync(channel.BeginGetFoos, channel.EndGetFoos, null));
  12.  
  13.     var foos = await asyncGet.ExecuteAsync(q => q
  14.             .Where(f => f.Id > 3 && f.Id < 10)
  15.             .Skip(1)
  16.             .Take(3)
  17.             .OrderByDescending(f => f.Name));
  18.  
  19.     foreach (var foo in foos)
  20.     {
  21.         Console.WriteLine(foo.Name);
  22.     }
  23.  
  24.     channel.Close();
  25. }

 

Implementation Bits

If you like to read a short description of the underlying implementation then keep reading, otherwise skip this part.

Basically, when you define the ‘QueryableGet’, it provides you with an IQueryable model that is built using Linq2Rest which its query provider builds a URI out of your expression.
Linq2Rest allows you to plug-in your own ‘RestClient’ and ‘SerializerFactory’ which it then activates to get the data and serialize the results. This enabled me to provide my own implementations where I simply inject the URI query into a message header, call the service operation which the developer defined as part of the ‘QueryableGet’ and return the results.

On the service end, the ‘QueryableAttribute’ injects an operation invoker which translates the query located in the message header back on to the queryable model returned from the service operation.

.. And that’s about it.

Last Notes

You can download the source code if you like and experiment with it yourself, it’s quite cool I must say.
In this sample I created queries against a service with NetTcpBinding.

Lastly, I’m afraid some of the operators are not yet supported.
For example, I couldn’t get ‘Expand’ to affect anything, even when working with Entity Framework. Plus, ‘Contains’ as part of a filter expression seems to be unsupported in the current version of Linq2Rest.

Add comment
facebook linkedin twitter email

Leave a Reply

Your email address will not be published.

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

*