How to convert an OData query string to .NET expression tree

混江龙づ霸主 提交于 2020-02-17 07:41:12

问题


Completely rewriting this question since I understand more now than I did before.

I am attempting to abstract out the conversion of an OData query string directly to .NET expression tree. There appear to be a number of questions and articles on this, but no answers that provide an abstract solution that relies soley on the Microsoft.Data.OData namespace (ie, all examples rely on WebAPI, Entity Framework, or some other library).

The answer that does the best at providing an abstract solution is here:

https://stackoverflow.com/a/21536369/701346

These two lines got me started:

IEdmModel model = EdmxReader.Parse(new XmlTextReader(/*stream of your $metadata file*/));
IEdmEntityType type = model.FindType("organisation");

After much toil, I've learned that OData requires an EDM in order to generate its own proprietary expression tree model. It's only a model. You have to traverse that model to ultimately generate your own expression tree.

So I've done all of this (sort of). I happened upon this article which showed me how to create a basic EDM without any navigation:

https://blogs.msdn.microsoft.com/alexj/2012/12/06/parsing-filter-and-orderby-using-the-odatauriparser/

Using that, I ended up creating an EDM generator that recursively reflects through a class to build the EDM. The problem is that this is ridiculously complex and there isn't much information online about how to create an EDM dynamically, so it doesn't define any navigation properties and only works with a single entity.

I then created an ODataExpressionVisitor that's modeled after System.Linq.Expressions.ExpressionVisitor. It works pretty good. It's able to take this OData query string:

var filter = ODataUriParser.ParseFilter(
    "(Name eq 'Oxford Mall' or Street eq '123 whatever ln') and Id eq 2",
    edmBuilder.Model, edmBuilder.Model.FindType(typeof(CustomerLocation).FullName));

And generate this expression:

(
    $CustomerLocation.Name == "Oxford Mall" || 
    $CustomerLocation.Street == "123 whatever ln"
) && 
$CustomerLocation.Id == 2

It works, too, because I can compile it to a delagate and pass a CustomerLocation object into it and it will return the proper true/false. I haven't tested it with EF6 or my other expression-based framework yet, though.

However, I think I am recreating a wheel here. There must be an existing means to 1) generate an convention-based EDM from a class alone and 2) convert the resulting OData expression tree to a .NET expression tree.


回答1:


I hat the same issue a while ago and solved it with the following approach:

  1. Create ODataQueryOptions from an ODATA query string, which can be achieved by constructing it from a URI (I described this in more detail with code examples in Modifying ODataQueryOptions on the fly
  2. To convert the various parts of ODataQueryOptions like FilterQueryOption to an Expression (actually a MethodCallExpression) we can use ApplyTo in conjunction with an empty IQueryable (converting OrderByQueryOption is quite similar to this):

    public static Expression ToExpression<T>(this FilterQueryOption filterQueryOption)
    where T: class
    {
        IQueryable queryable = Enumerable.Empty<T>().AsQueryable();
        queryable = filterQueryOption.ApplyTo(queryable, new OdataQuerySettings());
    
        return queryable.Expression;
    }
    
  3. Converting $skip and $top is as easy as using Skip() and Take().

  4. Depending on the scenario, in order to make this expression usable as a Lambda we might have to use an ExpressionVisitor that replaces the argument of the MethodCallExpression.
  5. Creating a Lambda is then only a matter of taking the Body and the Parameter and create a new Expression.Lambda:

    var expressionLambda = Expression.Lambda<Func<T, bool>>
        (
            visitedMethodCallExpression.Body, 
            Expression.Parameter(typeof(T), "$it")
        );
    

For a more complete example see Converting ODataQueryOptions into LINQ Expressions in C# (but this uses EntityFramework in the end, so you might want to skip the rest).



来源:https://stackoverflow.com/questions/40089918/how-to-convert-an-odata-query-string-to-net-expression-tree

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!