如何将OData查询字符串转换为.NET表达式树

时间:2016-10-17 15:10:11

标签: .net expression odata

完全重写这个问题,因为我现在比以前更了解这个问题。

我试图将OData查询字符串的转换直接抽象为.NET表达式树。关于此问题似乎有很多问题和文章,但没有答案提供依赖于<{1}}命名空间上的 soley 的抽象解决方案(即,所有示例都依赖于WebAPI,实体框架) ,或其他一些图书馆。)

在提供抽象解决方案方面做得最好的答案就在这里:

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

这两行让我开始了:

Microsoft.Data.OData

经过多次努力,我了解到OData需要EDM才能生成自己专有的表达式树模型。这只是一个模特。您必须遍历该模型才能最终生成自己的表达式树。

所以我已经做了所有这些(有点)。我碰巧在这篇文章中向我展示了如何在没有任何导航的情况下创建基本的EDM:

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

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

然后我创建了一个以IEdmModel model = EdmxReader.Parse(new XmlTextReader(/*stream of your $metadata file*/)); IEdmEntityType type = model.FindType("organisation"); 为模型的ODataExpressionVisitor。它的效果非常好。它可以使用此OData查询字符串:

System.Linq.Expressions.ExpressionVisitor

并生成此表达式:

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

它也有效,因为我可以将它编译为delagate并将( $CustomerLocation.Name == "Oxford Mall" || $CustomerLocation.Street == "123 whatever ln" ) && $CustomerLocation.Id == 2 对象传递给它,它将返回正确的true / false。不过,我还没有使用EF6或其他基于表达式的框架进行测试。

然而,我想我在这里重新创造一个轮子。必须有一种现有方法1)仅从类生成基于约定的EDM,2)将生成的OData表达式树转换为.NET表达式树。

1 个答案:

答案 0 :(得分:3)

我前段时间遇到同样的问题,并采用以下方法解决了这个问题:

  1. 从ODATA查询字符串创建ODataQueryOptions,这可以通过从URI构建它来实现(我在Modifying ODataQueryOptions on the fly
  2. 中使用代码示例更详细地描述了这一点)
  3. 要将ODataQueryOptions的{​​{1}}的各个部分转换为FilterQueryOption(实际上是Expression),我们可以将MethodCallExpressionApplyTo结合使用空IQueryable(转换OrderByQueryOption与此非常相似):

    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;
    }
    
  4. 转换$skip$top就像使用Skip()Take()一样简单。

  5. 根据具体情况,为了使此表达式可用作Lambda,我们可能必须使用ExpressionVisitor替换MethodCallExpression的参数。
  6. 创建Lambda只需要考虑BodyParameter并创建新的Expression.Lambda

    var expressionLambda = Expression.Lambda<Func<T, bool>>
        (
            visitedMethodCallExpression.Body, 
            Expression.Parameter(typeof(T), "$it")
        );
    
  7. 有关更完整的示例,请参阅Converting ODataQueryOptions into LINQ Expressions in C#(但最后使用EntityFramework,因此您可能希望跳过其余部分。)