LINQ表达式。类型的变量“P”从范围引用,但它没有定义 [英] LINQ expressions. Variable 'p' of type referenced from scope, but it is not defined

查看:112
本文介绍了LINQ表达式。类型的变量“P”从范围引用,但它没有定义的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我构建动态LINQ查询与此代码。
看来工作,但是当我在我的搜索有一个以上的搜索字符串,(所以当添加多个表达式,我得到以下错误:



类型的变量p的范围引用,但它没有定义



我想我只能定义一次/使用页。但是,如果是的话,我需要改变我的代码位。任何人都可以点我在这里向正确的方向?

 如果(searchStrings! = NULL)
{
的foreach(在searchStrings字符串搜索字符串)
{
表达式来; Func键<产品,布尔>> containsExpression = p => p.Name.Contains(搜索字符串);
filterExpressions.Add(containsExpression);
}
}

Func键<表达,表达,BinaryExpression> []运营商=新Func键<表达,表达,BinaryExpression> [] {} Expression.AndAlso;
表达式来; Func键<产品,布尔>>过滤器= this.CombinePredicates<产品及GT;(filterExpressions,运营商);

&IQueryable的LT;产品与GT;查询= cachedProductList.AsQueryable()式(过滤器)。

query.Take(itemLimit).ToList(); << **错误当查询执行**


公共表达式来; Func键< T,BOOL>> CombinePredicates< T>(IList的<表达式来; Func键< T,BOOL>>> predicateExpressions,Func键<表达,表达,BinaryExpression> logicalFunction)
{
表达式来; Func键< T,BOOL>>过滤= NULL;

如果(predicateExpressions.Count大于0)
{
表达式来; Func键< T,BOOL>> firstPredicate = predicateExpressions [0];
表达体= firstPredicate.Body;
的for(int i = 1; I< predicateExpressions.Count;我++)
{
=身体logicalFunction(身体,predicateExpressions [I]。体);
}
滤波器= Expression.Lambda<&Func键LT; T,BOOL>>(身体,firstPredicate.Parameters);
}

返回过滤器;
}


解决方案

简化,这里有几行您正在尝试做的(我用的字符串,而不是产品等,但思路是一样的):

 表达式来; Func键<字符串,布尔>> C1 = X => x.Contains(111); 
表达式来; Func键<字符串,布尔>> C2 = Y => y.Contains(222);
VAR总和= Expression.AndAlso(c1.Body,c2.Body);
VAR sumExpr = Expression.Lambda(总和,c1.Parameters);
sumExpr.Compile(); //这里的例外

请注意我是如何扩大你的foreach成两个表达式与x和y - 这是它究竟是如何看起来像编译器,是的不同的的参数



在换句话说,你正在尝试做这样的事情:

  X => x.Contains(...)及和放大器; y.Contains(...); 

和编译器不知道那是什么'Y'变量??



要解决这个问题,我们需要准确地使用相同的参数(不只是名字,还参考)所有表达式。我们可以解决这样此次简化代码:

 表达式来; Func键<字符串,布尔>> C1 = X => x.Contains(111); 
表达式来; Func键<字符串,布尔>> C2 = Y => y.Contains(222);
变种总和= Expression.AndAlso(c1.Body,Expression.Invoke(C2,c1.Parameters [0])); //这里是神奇的
VAR sumExpr = Expression.Lambda(总和,c1.Parameters);
sumExpr.Compile(); // OK



所以,解决您原来的代码会是这样:

 静态内部类节目
{
公共类产品
{
公共字符串名称;
}

私有静态无效的主要(字串[] args)
{
变种searchStrings =新[] {111,222};
变种cachedProductList =新的List<产品与GT;
{
新产品{名称=111不应该匹配},
新产品{名称=222不应该匹配},
新产品{名称=111 222应该匹配},
};

变种filterExpressions =新的List<表达式来; Func键<产品,布尔>>>();
的foreach(在searchStrings字符串搜索字符串)
{
表达式来; Func键<产品,布尔>> containsExpression = X => x.Name.Contains(搜索字符串); //不好
filterExpressions.Add(containsExpression);
}

VAR过滤器= CombinePredicates<产品及GT;(filterExpressions,Expression.AndAlso);

VAR的查询= cachedProductList.AsQueryable()式(过滤器)。

无功名单= query.Take(10).ToList();
的foreach(在列表VAR产品)
{
Console.WriteLine(product.Name);
}
}

公共静态表达式来; Func键< T,BOOL>> CombinePredicates< T>(IList的<表达式来; Func键< T,BOOL>>> predicateExpressions,Func键<表达,表达,BinaryExpression> logicalFunction)
{
表达式来; Func键< T,BOOL>>过滤= NULL;

如果(predicateExpressions.Count大于0)
{
变种firstPredicate = predicateExpressions [0];
表达体= firstPredicate.Body;
的for(int i = 1; I< predicateExpressions.Count;我++)
{
=身体logicalFunction(身体,Expression.Invoke(predicateExpressions [I],firstPredicate.Parameters));
}
滤波器= Expression.Lambda<&Func键LT; T,BOOL>>(身体,firstPredicate.Parameters);
}

返回过滤器;
}
}



但要注意的输出:

  222不应该匹配
111 222应该匹配

不是你可能期望..这是使用的foreach searchString的,应以下列方式进行重写的结果是:

  ... 
的foreach(在searchStrings字符串搜索字符串)
{
变量名称=搜索字符串;
表达式来; Func键<产品,布尔>> containsExpression = X => x.Name.Contains(名);
filterExpressions.Add(containsExpression);
}
...

和这里是输出:



  111 222应该匹配


I'm building a LINQ query dynamically with this code. It seems to work, but when i have more than one searchString in my search, (so when multiple expressions are added, i get the following error:

Variable 'p' of type referenced from scope, but it is not defined

I guess i can only define /use p once. But, if so, i need to alter my code a bit. Can anyone point me in the right direction here?

    if (searchStrings != null)
    {
        foreach (string searchString in searchStrings)
        {
            Expression<Func<Product, bool>> containsExpression = p => p.Name.Contains(searchString);
            filterExpressions.Add(containsExpression);
        }
    }

    Func<Expression, Expression, BinaryExpression>[] operators = new Func<Expression, Expression, BinaryExpression>[] { Expression.AndAlso };
    Expression<Func<Product, bool>> filters = this.CombinePredicates<Product>(filterExpressions, operators);

    IQueryable<Product> query = cachedProductList.AsQueryable().Where(filters);

    query.Take(itemLimit).ToList();  << **error when the query executes**


    public Expression<Func<T, bool>> CombinePredicates<T>(IList<Expression<Func<T, bool>>> predicateExpressions, Func<Expression, Expression, BinaryExpression> logicalFunction)
    {
        Expression<Func<T, bool>> filter = null;

        if (predicateExpressions.Count > 0)
        {
            Expression<Func<T, bool>> firstPredicate = predicateExpressions[0];
            Expression body = firstPredicate.Body;
            for (int i = 1; i < predicateExpressions.Count; i++)
            {
                body = logicalFunction(body, predicateExpressions[i].Body);
            }
            filter = Expression.Lambda<Func<T, bool>>(body, firstPredicate.Parameters);
        }

        return filter;
    }

解决方案

Simplifying, here are several lines which you are trying to do (I use string instead Product etc, but idea is the same):

        Expression<Func<string, bool>> c1 = x => x.Contains("111");
        Expression<Func<string, bool>> c2 = y => y.Contains("222");
        var sum = Expression.AndAlso(c1.Body, c2.Body);
        var sumExpr = Expression.Lambda(sum, c1.Parameters);
        sumExpr.Compile(); // exception here

Please notice how I expanded your foreach into two expressions with x and y - this is exactly how it looks like for compiler, that are different parameters.

In other words, you are trying to do something like this:

x => x.Contains("...") && y.Contains("...");

and compiler wondering what is that 'y' variable??

To fix it, we need to use exactly the same parameter (not just name, but also reference) for all expressions. We can fix this simplified code like this:

        Expression<Func<string, bool>> c1 = x => x.Contains("111");
        Expression<Func<string, bool>> c2 = y => y.Contains("222");
        var sum = Expression.AndAlso(c1.Body, Expression.Invoke(c2, c1.Parameters[0])); // here is the magic
        var sumExpr = Expression.Lambda(sum, c1.Parameters);
        sumExpr.Compile(); //ok

So, fixing you original code would be like:

internal static class Program
{
    public class Product
    {
        public string Name;
    }

    private static void Main(string[] args)
    {
        var searchStrings = new[] { "111", "222" };
        var cachedProductList = new List<Product>
        {
            new Product{Name = "111 should not match"},
            new Product{Name = "222 should not match"},
            new Product{Name = "111 222 should match"},
        };

        var filterExpressions = new List<Expression<Func<Product, bool>>>();
        foreach (string searchString in searchStrings)
        {
            Expression<Func<Product, bool>> containsExpression = x => x.Name.Contains(searchString); // NOT GOOD
            filterExpressions.Add(containsExpression);
        }

        var filters = CombinePredicates<Product>(filterExpressions, Expression.AndAlso);

        var query = cachedProductList.AsQueryable().Where(filters);

        var list = query.Take(10).ToList();
        foreach (var product in list)
        {
            Console.WriteLine(product.Name);
        }
    }

    public static Expression<Func<T, bool>> CombinePredicates<T>(IList<Expression<Func<T, bool>>> predicateExpressions, Func<Expression, Expression, BinaryExpression> logicalFunction)
    {
        Expression<Func<T, bool>> filter = null;

        if (predicateExpressions.Count > 0)
        {
            var firstPredicate = predicateExpressions[0];
            Expression body = firstPredicate.Body;
            for (int i = 1; i < predicateExpressions.Count; i++)
            {
                body = logicalFunction(body, Expression.Invoke(predicateExpressions[i], firstPredicate.Parameters));
            }
            filter = Expression.Lambda<Func<T, bool>>(body, firstPredicate.Parameters);
        }

        return filter;
    }
}

But notice the output:

222 should not match
111 222 should match

Not something you may expect.. This is result of using searchString in foreach, which should be rewritten in the following way:

        ...
        foreach (string searchString in searchStrings)
        {
            var name = searchString;
            Expression<Func<Product, bool>> containsExpression = x => x.Name.Contains(name);
            filterExpressions.Add(containsExpression);
        }
        ...

And here is output:

111 222 should match

这篇关于LINQ表达式。类型的变量“P”从范围引用,但它没有定义的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆