为过滤器的多个条件创建动态linq表达式OData包含?

时间:2015-10-21 08:52:14

标签: c# .net linq odata

对于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();

我无法执行上述声明。

2 个答案:

答案 0 :(得分:1)

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

这样做,迭代一个字典,它定义了一个searchterm和一个可以查看的属性。

然后我们构建一个谓词并迭代每个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();

输出继电器: enter image description here

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

Linqpad source

//编辑:这是一个构建表达式的静态方法。您只需要通过搜索字词提供Dictionary<string,string>

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

答案 1 :(得分:1)

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

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

其次,您可以堆叠多个Filter子句,但它们将使用&#34; AND&#34;进行组合。运营商。你需要&#34;或&#34;。

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

如果必须从一组表达式逐步构建Filter子句,那么使用当前版本的Simple.OData.Client实现它的唯一方法是将字符串发送到Filter,并且可以逐步构建该字符串。您甚至可以使用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();
相关问题