本文介绍了如何将OData查询字符串转换为.NET表达式树的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



我正在尝试将OData查询字符串的转换直接转换为.NET表达式,因此我完全重写了此问题。树。似乎有一些问题和文章,但没有答案,提供一个抽象的解决方案,依赖于 Microsoft.Data.OData 命名空间(即,所有示例都依赖于WebAPI,Entity Framework或其他库)。



提供抽象解决方案的最佳答案是: / p>



使用它,我最终创建了一个EDM生成器,通过一个类递归地反映建立EDM。问题是,这是非常复杂的,并且没有太多关于如何动态创建EDM的在线信息,因此它没有定义任何导航属性,仅适用于单个实体。



然后,我创建了一个在 System.Linq.Expressions.ExpressionVisitor 之后建模的ODataExpressionVisitor。它的作品相当不错可以使用这个OData查询字符串:

  var filter = ODataUriParser.ParseFilter(
(Name eq'Oxford商城'或街道'123无论ln')和Id eq 2,
edmBuilder.Model,edmBuilder.Model.FindType(typeof(CustomerLocation).FullName));

并生成此表达式:


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

它也可以编译到一个delagate并将 CustomerLocation 对象传递给它,它将返回正确的true / false。我没有使用EF6或其他基于表达式的框架进行测试,但是,但是,我想我在这里重新创建一个轮子。必须有现有的方法1)从类中单独生成基于约会的EDM,2)将生成的OData表达式树转换为.NET表达式树。

解决方案

我在同一个问题上解决了以下方法:


  1. 创建 ODataQueryOptions 来自一个ODATA查询字符串,可以通过从URI中构建它来实现(我用

  2. 要将 ODataQueryOptions 的各个部分转换为表达式 $ c>(实际上是一个 MethodCallExpression )我们可以使用 ApplyTo 结合一个空的 IQueryable (转换 OrderByQueryOption 与此类似):

      public static Expression ToExpression< T>(此FilterQueryOption filterQueryOption)
    其中T:class
    {
    IQueryable可查询= Enumerable.Empty< T>()。AsQueryable();
    queryable = filterQueryOption.ApplyTo(可查询,新的OdataQuerySettings());

    return queryable.Expression;
    }


  3. 转换 $ skip 和 $ top 与使用 Skip()和 Take()一样简单, 。


  4. 根据场景,为了使此表达式可用作Lambda,我们可能必须使用表达式Visitor 替换 MethodCallExpression 的参数。

  5. 创建一个Lambda只是一个使用正文和参数并创建一个新的 Expression.Lambda :

      var expressionLambda = Expression.Lambda< Func< T,bool>> 

    visitedMethodCallExpression.Body,
    Expression.Parameter(typeof(T),$ it)
    );


有关更完整的示例,请参阅将ODataQueryOptions转换为C#中的LINQ表达式(但是最后使用EntityFramework,所以你可能想跳过其余的)。


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.

解决方案

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).

这篇关于如何将OData查询字符串转换为.NET表达式树的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-28 20:07