Will Gant
Will Gant

Reputation: 583

Parsing ODataQueryOptions<type> without EF/Nhibernate

I have a project with a large codebase that uses an in-house data access layer to work with the database. However, we want to support OData access to the system. I'm quite comfortable with expression trees in C#. How do I get at something I can parse here in order to get the structure of their actual query?

Is there a way to get an AST out of this thing that I can turn into sql code?

Upvotes: 1

Views: 2208

Answers (2)

lencharest
lencharest

Reputation: 2935

You can use ODataQueryOptions<T> to get abstract syntax trees for the $filter and $orderby query options. ($skip and $top are also available as parsed integers.) Since you don't need/want LINQ support, you could then simply pass the ASTs to a repository method, which would then visit the ASTs to build up the appropriate SQL stored proc invocation. You will not call ODataQueryOptions.ApplyTo. Here's a sketch:

public IEnumerable<Thing> Get(ODataQueryOptions<Thing> opts)
{
    var filter = opts.Filter.FilterClause.Expression;
    var ordering = opts.OrderBy.OrderByClause.Expression;
    var skip = opts.Skip.Value;
    var top = opts.Top.Value;

    return this.Repository.GetThings(key, filter, ordering, skip, top);
}

Note that filter and ordering in the above are instances of Microsoft.OData.Core.UriParser.Semantic.SingleValueNode. That class has a convenient Accept<T> method, but you probably do not want your repository to depend on that class directly. That is, you should probably use a helper to produce an intermediate form that is independent of Microsoft's OData implementation.

If this is a common pattern, consider using parameter binding so you can get the various query options directly from the controller method's parameter list.

Upvotes: 1

Serge Semenov
Serge Semenov

Reputation: 10042

Essentially, you need to implement you own Query Provider which known how to translate the expression tree to an underlying query.

A simplified version of a controller method would be:

    [ODataRoute("foo")]
    public List<Foo> GetFoo(ODataQueryOptions<Foo> queryOptions)
    {
        var queryAllFoo = _myQueryProvider.QueryAll<Foo>();
        var modifiedQuery = queryOptions.ApplyTo(queryAllFoo);
        return modifiedQuery.ToList();
    }

However!

  1. This is not trivial, it took me about 1 month to implement custom OData query processing
  2. You need to build the EDM model, so the WebApi OData can process and build right expression trees
  3. It might involve reflection, creation of types at runtime in a dynamic assembly (for the projection), compiling lambda expressions for the best performance
  4. WebAPI OData component has some limitations, so if you want to get relations working, you need to spend much more extra time, so in our case we did some custom query string transformation (before processing) and injecting joins into expression trees when needed
  5. There are too many details to explain in one answer, it's a long way..

Good luck!

Upvotes: 2

Related Questions