How to Handle Query Expressions for Untyped Values?

Tags: Linq2Rest, LINQ, REST, Types

One of the first decisions when starting Linq2Rest was that it was not going to follow the elaborate type description rules in the OData standard. Even though it may seem a bit schizophrenic Linq2Rest was intended to allow a uniform query syntax against a RESTful service. One of the core concepts of REST is that it is untyped - at least according to Roy Fielding, and he should know. So even though Linq2Rest attempts to follow the OData syntax there is some deliberate deviation.

The OData standard goes to great lengths to bring a defined navigation structure and type descriptions into the response data. Either one can follow along with this design decision and get the benefits of a rich data transfer standard, or one can stay in the nebular world of REST. Having decided to lean towards REST the question then arises how to apply the query expression that the client sends to the server.

In the current version Linq2Rest does not impose any kind of type sharing between client and server. However this is not exactly true, since the query expression is being applied on the server to an object which is expected to have the same shape as on the client side (at least for the passed parameters). Actually it's even more convoluted since types can implement aliased properties, but that's a different story. Lets assume that you send a query syntax like this one:

FirstName eq 'Bart' and Age eq 10

The expectation is that the value on the server has a FirstName and an Age property and that both must match the conditions. What is not clear is whether the client cares if there is a LastName property. It becomes even more murky if the query was using an OR condition instead of AND. Would the client care if the server value did not contain an Age property as long as the FirstName property matched?

If we look at query parameters for HTTP requests then it is clearly OK to add undefined query parameters. Try sending a request to http://www.google.com?gobbledigook=a. It will handle this gracefully by ignoring the unknown query parameter. In the same way JavaScript is quite forgiving in how it handles missing properties. If we consider the following JavaScript code snippet:

var x = { "a" : 1};
if(x.a == 1 || x.b != 2) { alert("Success") }

If you run that code you will get a nice alert box, which means that JavaScript handles missing properties in a very relaxed way (I don't want to get into the muddy type system of JavaScript).

I think the world of the web is clearly leaning towards a loose handling of unknown properties. But transferring that back to a typed world is not all that easy because now a query expression needs to evaluate whether a given condition can safely be ignored. In other words, when building up the expression tree, the builder has to check whether a particular branch can safely be applied, whether it should be ignored or whether it should invalidate the whole query and simply return nothing.

Another point to consider, but less important, is what the expectation of the caller is. Linq2Rest is primarily being used in .NET environments with matching types on the client and server. Having a loose type handling means that the expression tree will suffer from the leaking abstraction of the web world where the client may want to have a query provider which behaves as any other LINQ to Objects provider. The client would generate the query based on a typed expression tree. If for some reason the type signature had changed on the client and no results were returned instead of notifying of a mismatch in type signatures, then the client may be at a loss as to why this is.

I'm not sure there is a clear answer and the result may be that it will be necessary to implement a strict mode for those wishing to lean closer to the OData standard

Latest Tweets