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

osc*_*tin 8 .net expression odata

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

我试图抽象出OData查询字符串直接转换为.NET表达式树.似乎有一些关于这个问题和文章,但没有提供一个依赖一个抽象的解决答案索利Microsoft.Data.OData命名空间(即,所有的例子依靠的WebAPI,实体框架,或其他一些库).

在提供抽象解决方案方面做得最好的答案如下:

/sf/answers/1507545861/

这两行让我开始:

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

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

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

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

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

然后我创建了一个模仿的ODataExpressionVisitor System.Linq.Expressions.ExpressionVisitor.它的效果非常好.它能够获取此OData查询字符串:

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));
Run Code Online (Sandbox Code Playgroud)

并生成此表达式:

(
    $CustomerLocation.Name == "Oxford Mall" || 
    $CustomerLocation.Street == "123 whatever ln"
) && 
$CustomerLocation.Id == 2
Run Code Online (Sandbox Code Playgroud)

它也可以工作,因为我可以将它编译为一个delagate并将一个CustomerLocation对象传递给它,它将返回正确的true/false.不过,我还没有使用EF6或其他基于表达式的框架进行测试.

但是,我想我在这里重新制作一个轮子.必须存在以下方法:1)仅从类生成基于约定的EDM,以及2)将得到的OData表达式树转换为.NET表达式树.

小智 5

不久前我遇到了同样的问题,并通过以下方法解决了它:

  1. ODataQueryOptions从 ODATA 查询字符串创建,这可以通过从 URI 构造它来实现(我在动态修改 ODataQueryOptions 中使用代码示例更详细地描述了这一点
  2. 要将ODataQueryOptionslike的各个部分转换FilterQueryOption为 an Expression(实际上是 a MethodCallExpression),我们可以将其ApplyTo与 empty 结合使用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;
    }
    
    Run Code Online (Sandbox Code Playgroud)
  3. 转换$skipand$top就像使用Skip()and一样简单Take()

  4. 根据场景,为了使该表达式可用作 Lambda,我们可能必须使用ExpressionVisitor替换 的参数MethodCallExpression
  5. 创建一个 Lambda 只需要使用BodyParameter并创建一个新的Expression.Lambda

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

有关更完整的示例,请参阅将 ODataQueryOptions 转换为 C# 中的 LINQ 表达式(但这最终使用了 EntityFramework,因此您可能希望跳过其余部分)。