简体   繁体   English

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

[英]Creating dynamic linq expression OData for multiple conditions for filter Contains?

For a single Contains filter condition in Odata I tried the following: 对于Odata中的一个Contains过滤条件,我尝试了以下操作:

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

How can I use multiple Contains filters? 如何使用多个Contains过滤器?

For example: 例如:

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

I tried with query expression using this code . 我尝试使用此代码使用查询表达式。

But, how do I pass filter expression inside Odata .Filter() ? 但是,如何在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();

I want to use deleg expression and pass to Odata. 我想使用deleg表达式并将其传递给deleg

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

I'm unable to execute the above statement. 我无法执行以上声明。

I don't have your datamodel so I used the Northwind OData feed to build a solution for you. 我没有您的数据模型,所以我使用了罗斯文OData提要为您构建了一个解决方案。

What this does is, iterate over a dictionary, which defines a searchterm and a property in which to look. 这是在字典上进行迭代,该字典定义了搜索项和要查找的属性。

We then build a predicate and iterate over each kvp of this. 然后,我们建立一个谓词并对每个kvp进行迭代。 Finally we return a lambda func from this. 最后,我们从中返回一个lambda函数。 Think of it as a predicatebuilder in a loop: 将其视为循环中的谓词构建器:

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

Ouput: 输出继电器: 在此处输入图片说明

The only thing left for your to do is replace the Category against your target type. 剩下要做的唯一事情就是将Category替换为目标类型。 If time allows it, I'll wrap this in a generic method and add it to this answer. 如果时间允许,我将其包装在通用方法中,并将其添加到此答案中。

Linqpad source Linqpad来源

//EDIT: Heres a static method to build your expression. //编辑:这是构建表达式的静态方法。 You only need to provde a Dictionary<string,string> witht he search terms. 您只需要提供带有搜索词的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;
}

Usage: 用法:

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

First, Simple.OData.Client has its own LINQ expression parser, so everything that comes to Filter clause is sent to its custom parser that is far more limited than the one built into C# (aka LINQ-to-objects). 首先,Simple.OData.Client拥有自己的LINQ表达式解析器,因此Filter子句中的所有内容都将发送到其自定义解析器,该解析器比C#内置的自定义解析器(即LINQ-to-objects)要受限制的多。 And it's limited for good reasons, because it can't provide more than provided by OData protocol. 而且由于有限的原因它受到限制,因为它不能提供比OData协议更多的内容。

So expressions like Filter deleg.ToString() will not work, you will have to write the explicit expression. 因此,诸如Filter deleg.ToString()类的表达式将不起作用,您将不得不编写显式表达式。

Second, you can stack multiple Filter clauses but they will be combined using "AND" operator. 其次,您可以堆叠多个Filter子句,但是它们将使用“ AND”运算符进行组合。 And you need "OR". 并且您需要“或”。

Third, the expression that you wrote (x => x.Name.Contains("john") || x.Address.Contains("india")) is a supported expression and should work. 第三,您编写的表达式(x => x.Name.Contains("john") || x.Address.Contains("india"))是受支持的表达式,应该可以使用。

If you must incrementally build Filter clause from a set of expressions then the only way to achive it using current version of Simple.OData.Client is to send a string to Filter, and that string can be built incrementally. 如果必须从一组表达式增量构建Filter子句,则使用当前版本的Simple.OData.Client实现它的唯一方法是将字符串发送给Filter,并且可以逐步构建该字符串。 You can even generate individual parts using Simple.OData.Client method GetCommandTextAsync() , then extract filter parts from them and concatenate. 甚至可以使用生成单个零件Simple.OData.Client方法GetCommandTextAsync()然后从中提取过滤嘴部分和串联。 I know, it's not elegant. 我知道,这并不优雅。

UPDATE: I just pushed the version 4.12 that exposes public constructor for ODataExpression. 更新:我刚刚推送了4.12版本,该版本公开了ODataExpression的公共构造函数。 So you can do things like these: 因此,您可以执行以下操作:

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM