【问题标题】:Creating dynamic linq expression OData for multiple conditions for filter Contains?为过滤器的多个条件创建动态 linq 表达式 OData 包含?
【发布时间】:2015-10-21 08:52:14
【问题描述】:

对于 Odata 中的单个 Contains 过滤条件,我尝试了以下操作:

  var customerData = await myclient.For<Customer>()
            .Filter(x => x.Name.Contains("john"))
            .FindEntriesAsync();

如何使用多个Contains 过滤器?

例如:

 var customerData = await myclient.For<Customer>()
            .Filter(x => x.Name.Contains("john") || x.Address.Contains("india"))
            .FindEntriesAsync();

我尝试使用 code 使用查询表达式。

但是,如何在 Odata .Filter() 中传递过滤器表达式?

List<Filter> filter = new List<Filter>()
{
    new Filter { PropertyName = "City" ,
        Operation = Op .Equals, Value = "Mitrovice"  },
    new Filter { PropertyName = "Name" ,
        Operation = Op .StartsWith, Value = "L"  },
    new Filter { PropertyName = "Salary" ,
        Operation = Op .GreaterThan, Value = 9000.0 }
};

var deleg = ExpressionBuilder.GetExpression<Person>(filter).Compile();

我想使用deleg 表达式并传递给Odata。

var customerData = await myclient.For<Customer>()
            .Filter(deleg.ToString())
            .FindEntriesAsync();

我无法执行上述语句。

【问题讨论】:

标签: c# .net linq odata


【解决方案1】:

首先,Simple.OData.Client 有自己的 LINQ 表达式解析器,因此 Filter 子句的所有内容都会发送到其自定义解析器,该解析器远比 C# 中内置的解析器(又名 LINQ-to-objects)受限。并且它受到限制是有充分理由的,因为它不能提供超出 OData 协议提供的更多内容。

所以像 Filter deleg.ToString() 这样的表达式将不起作用,您必须编写显式表达式。

其次,您可以堆叠多个过滤子句,但它们将使用“AND”运算符进行组合。你需要“或”。

第三,您编写的表达式(x =&gt; x.Name.Contains("john") || x.Address.Contains("india")) 是受支持的表达式,应该可以工作。

如果您必须从一组表达式增量构建过滤器子句,那么使用当前版本的Simple.OData.Client 实现它的唯一方法是将字符串发送到过滤器,并且可以增量构建该字符串。您甚至可以使用Simple.OData.Client 方法GetCommandTextAsync() 生成单个部分,然后从中提取过滤器部分并连接。我知道,这并不优雅。

更新:我刚刚推送了 4.12 版,它公开了 ODataExpression 的公共构造函数。因此,您可以执行以下操作:

Expression<Predicate<Product>> condition1 = x => x.ProductName == "Chai";
Expression<Func<Product, bool>> condition2 = x => x.ProductID == 1;
var filter = new ODataExpression(condition1);
filter = filter || new ODataExpression(condition2);
var result = await client.For<Product>.Filter(filter).FindEntriesAsync();

or

var filter = new ODataExpression<Product>(x => x.ProductName == "Chai");
filter = filter || new ODataExpression<Product>(x => x.ProductID == 1);
var result = await client.For<Product>.Filter(filter).FindEntriesAsync();

【讨论】:

  • 嗯,其实这里可以做点什么。在内部 Simple.OData.Client 使用 ODataExpression 类,它只是不公开其将 LINQ 表达式转换为 ODataExpression 的公共方法。我会考虑打开它的可能性。起来。
  • 非常感谢,如果公开后能给出任何代码 sn-p 那就太好了
  • @Neo,看看实现你需要的版本 4.12,看看我更新的答案。
  • 非常感谢这个新版本:)
【解决方案2】:

我没有您的数据模型,因此我使用 Northwind OData 提要为您构建解决方案。

它的作用是遍历一个字典,它定义了一个搜索词和一个要查找的属性。

然后我们构建一个谓词并遍历它的每个 kvp。最后我们从这里返回一个 lambda 函数。 将其视为循环中的谓词构建器:

/*using http://services.odata.org/V3/Northwind/Northwind.svc/ */

//Define a set of KeyValueParis to search for
var keywords = new Dictionary<string, string> {
    {"Beverages", "CategoryName"}, 
    {"savory", "Description"},
    {"meats", "Description"},
    {"Condiments", "CategoryName"}
};

//Create the predicate and initialize it
Expression<Func<Category, bool>> predicate = x => false;
//Define the type
ParameterExpression parameterExp = Expression.Parameter(typeof(Category), "Category");
//Get the Contains method. reference: http://stackoverflow.com/questions/278684/how-do-i-create-an-expression-tree-to-represent-string-containsterm-in-c
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });

//Iterate over each kvp
foreach (var kvp in keywords)
{
    var body = predicate.Body;
    //set the property or field we are checking against
    var memberExpr = Expression.PropertyOrField(parameterExp, kvp.Value);
    var constExpr = Expression.Constant(kvp.Key, typeof(string));
    var containsMethodExpr = Expression.Call(memberExpr, method, constExpr);

    body = Expression.OrElse(body, containsMethodExpr);

    predicate = Expression.Lambda<Func<Category, bool>>(body, parameterExp);
}

Categories.Where (predicate).Dump();

输出:

您唯一要做的就是将Category 替换为您的目标类型。如果时间允许,我会将其包装在一个通用方法中并将其添加到此答案中。

Linqpad source

//编辑: 这是构建表达式的静态方法。您只需要提供带有搜索词的Dictionary&lt;string,string&gt;

static Expression<Func<T, bool>> BuildExpression<T>(Dictionary<string, string> searchTerms)
{
    //Create the predicate and initialize it
    Expression<Func<T, bool>> predicate = x => false;
    //Define the type
    ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
    //Get the Contains method. reference: http://stackoverflow.com/questions/278684/how-do-i-create-an-expression-tree-to-represent-string-containsterm-in-c
    MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });

    //Iterate over each kvp
    foreach (var kvp in searchTerms)
    {
        var body = predicate.Body;
        //set the property or field we are checking against
        var memberExpr = Expression.PropertyOrField(parameterExp, kvp.Value);
        var constExpr = Expression.Constant(kvp.Key, typeof(string));
        var containsMethodExpr = Expression.Call(memberExpr, method, constExpr);

        body = Expression.OrElse(body, containsMethodExpr);

        predicate = Expression.Lambda<Func<T, bool>>(body, parameterExp);
    }
    return predicate;
}

用法:

var lambda = BuildExpression<Category>(keywords);
Categories.Where(lambda).Dump();

【讨论】:

  • 太棒了,这将是很大的帮助,但我使用的是简单的 odata。我还编辑了我的问题并添加了 codeproject 中给出的通用方法,但它特定于 linq 不能处理 Odata 表达式,如果你有时间,请检查一下。
  • 我可以在var customerData = await myclient.For&lt;Customer&gt;() .Filter(predicate.ToString()) .FindEntriesAsync(); 中传递predicate 吗??
  • Customer.Where(lambda).Dump(); 不包含Where 的定义
猜你喜欢
  • 2021-11-04
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-01-22
  • 1970-01-01
相关资源
最近更新 更多