组合多个表达式(Expression <func <t,bool>&gt;)不使用变量。为什么呢?</FUNC <t时,布尔>

时间:2009-12-04 03:39:58

标签: linq lambda expression-trees foreach

好的家伙,和我一起露面。我先总结一下,然后再详细说明。

我编写了许多方法(.WhereOr,.WhereAnd),这些方法基本上允许我“堆叠”一堆lambda查询,然后将它们应用到集合中。例如,数据集的用法有点像这样(虽然它可以通过使用泛型与任何类一起使用):

WITH LINQ TO DATASETS(使用.NET DataSetExtensions)

DataTable Result;

List<Expression<Func<DataRow, bool>> Queries = new List<Expression<Func<DataRow, bool>>();

Queries.Add(dr=> dr.Field<string>("field1") == "somestring");
Queries.Add(dr=> dr.Field<string>("field2") == "somestring"); 
Queries.Add(dr=> dr.Field<string>("field3") == "somestring"); 

Result = GetSomeTable().AsEnumarable().WhereOr(Queries).CopyToDataTable();

现在说在上面的例子中,集合中只有一行匹配“somestring”,而它位于字段“field2”上。

这意味着Result的计数应为1。

现在,我说稍微重写以上代码:

DataTable Result;

List<Expression<Func<DataRow, bool>> Queries = new List<Expression<Func<DataRow, bool>>();

List<string> columns = new string[]{"field1","field2","field3"}.ToList();

string col;

foreach(string c in columns){
    col = c;
    Queries.Add(dr=> dr.Field<string>(col) == "somestring");
}

Result = GetSomeTable().AsEnumarable().WhereOr(Queries).CopyToDataTable();

现在,我并不真正理解表达,但对我来说,上面的两个例子都做了完全相同的事情。

第一个示例中的“Result”的计数为1,第二个示例中的“Result”的计数为0。

此外,在第二个示例的List列中,如果将“field2”设置为last,而不是second,则“Result”的计数正确为1。

所以,从这一切来看,我得出了一个结论,但我真的不明白发生了什么,也不知道如何解决它......?我可以更早地“评估”那些表达式......还是其中的一部分?

结论:

基本上,似乎,如果我将文字值发送到那里,比如“field1”,它就可以了。但是如果我发送变量,比如“col”,它就不起作用,因为这些“表达式”只会在代码中稍后进行评估。

这也可以解释为什么当我将“field2”移动到最后位置时它的工作原理。它的工作原理是因为变量“col”最后被分配给“field2”,因此当表达式评估“col”等于“field2”时。

好的,那么,有什么方法吗?

这是我的WhereOr方法的代码(它是IENumerable的扩展方法):

public static IQueryable<T> WhereOr<T>(this IEnumerable<T> Source, List<Expression<Func<T, bool>>> Predicates) {

        Expression<Func<T, bool>> FinalQuery;

        FinalQuery = e => false;

        foreach (Expression<Func<T, bool>> Predicate in Predicates) {
            FinalQuery = FinalQuery.Or(Predicate);
        }

        return Source.AsQueryable<T>().Where(FinalQuery);
    }

public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> Source, Expression<Func<T, bool>> Predicate) {
        InvocationExpression invokedExpression = Expression.Invoke(Predicate, Source.Parameters.Cast<Expression>());
        return Expression.Lambda<Func<T, bool>>(Expression.Or(Source.Body, invokedExpression), Source.Parameters);
    }

4 个答案:

答案 0 :(得分:6)

答案 1 :(得分:6)

“如何修复”答案。改变这个:

string col;
foreach(string c in columns) {
    col = c;
    Queries.Add(dr=> dr.Field<string>(col) == "somestring");
} 

到此:

foreach(string c in columns) {
    string col = c;
    Queries.Add(dr=> dr.Field<string>(col) == "somestring");
} 

享受。布莱恩给出了“什么和为什么”的答案。

答案 2 :(得分:1)

哦,在我评论之后,我看到了这个问题。您在循环的每次迭代中使用相同的变量“col”。构建lambda表达式时,它不绑定到变量的值,它引用变量本身。当您执行查询时,“Col”被设置为它的最后一个值。尝试在循环内创建一个临时字符串,设置其值,然后使用它。

答案 3 :(得分:0)

我找到了很好的文章来满足你的要求 该链接提供And<T>Or<T>扩展方法来制作它。

http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx