7

I'm trying to extract the filter expression from ODataQueryOptions so that I can use it in my business logic class.

public PageResult<Poco> Get(ODataQueryOptions odataQueryOptions)
{
    Expression<Func<Poco, bool>> myExpression = ... // what do i do here?

    var result = _myBusinessLogic.Search(myExpression);
    return new PageResult<Poco>(result, null, null);
}

I took a look at the blog describing translating the query into HQL here and I think (at least I hope) that's an overkill for what I'm trying to do.

I basically need to get the filter expression in the Expression<Func<Poco, bool>> form. I tried playing with ApplyTo() but I can't quite get it. Any help appreciated.

Iman Mahmoudinasab
  • 6,861
  • 4
  • 44
  • 68
boris
  • 1,525
  • 3
  • 13
  • 18

3 Answers3

12

We have a FilterBinder class that suits your needs but is internal unfortunately. Nevertheless you could do a simple trick to get hold of the $filter expression,

public static class ODataQueryOptionsExtensions
{
    public static Expression ToExpression<TElement>(this FilterQueryOption filter)
    {
        IQueryable queryable = Enumerable.Empty<TElement>().AsQueryable();
        queryable = filter.ApplyTo(queryable, new ODataQuerySettings());
        return queryable.Expression;
    }
}

In your case, you can just do,

public PageResult<Poco> Get(ODataQueryOptions odataQueryOptions)
{
    Expression<Func<Poco, bool>> myExpression = odataQueryOptions.Filter.ToExpression<Poco>();

    var result = _myBusinessLogic.Search(myExpression);
    return new PageResult<Poco>(result, null, null);
}

Notice that the expression contains looks more like this, SOTests.Customer[].Where($it => conditional-expression). So, you might have to extract that conditional expression from the lambda.

Iman Mahmoudinasab
  • 6,861
  • 4
  • 44
  • 68
RaghuRam Nadiminti
  • 6,718
  • 1
  • 33
  • 30
  • 1
    Thanks, I ended up doing something similar. It just looks like there's quite a bit of surgery required to get the filter expression out of the results of ApplyTo - casting to MethodCallExpression, extracting arguments and operands, etc. Slightly worried about long term stability of this code. – boris May 08 '13 at 19:58
  • I am not exposing the context to the application layer (api), and I would like to leverage ODataQueryOptions and related classes to be able to apply filters, pagination, navigation and so on, in my own data layer. Ideally, I would like to be able to extract all the expressions I could then apply to my queriable, passing those as parameter down to the lower layers of the application. The option I have so far is to pass the ODataQueryOptions, but this would couple my business and data layer to data, which I don't want. Do you have an example maybe on GitHub that could help me? – Norcino Mar 04 '18 at 19:30
  • 3
    The proposed solution doesn't work for me, I get back a `MethodCallExpression` from `queryable.Expression` which can't be casted to a `Expression>` like in the example. – Kyle V. Mar 22 '19 at 18:54
  • @KyleV., reference casting instructions [here](https://stackoverflow.com/a/55344775/6210068). Worked like a charm for me. TL;DR; `var expression = (Expression>)((UnaryExpression)call.Arguments[1]).Operand;` – Lance Sep 13 '20 at 17:02
1

In more recent versions of OData, FilterBinder isn't internal anymore. You can do the following directly:

public static class ODataQueryOptionsExtensions
{
    public static Expression ToExpression<TElement>(this FilterQueryOption filter)
    {
        var binderContext = new QueryBinderContext(model, new ODataQuerySettings(), typeof(TElement));
        var expression = new FilterBinder().BindFilter(filter.FilterClause, binderContext);
    }
}
Arithmomaniac
  • 4,604
  • 3
  • 38
  • 58
-1

I've implemented an $count action that uses OData $filter to query my EF Database. It's based on @raghuram-nadiminti answer. Thank you!

[HttpGet("$count")]
public virtual async Task<int> Count(ODataQueryOptions<MyBookEntity> odataQueryOptions)
{
    var queryable = this.dataContext.MyBooks;

    return await odataQueryOptions.Filter
        .ApplyTo(queryable, new ODataQuerySettings())
        .Cast<MyBookEntity>()
        .CountAsync();
}
Murilo Maciel Curti
  • 2,677
  • 1
  • 21
  • 26