为什么我的linq别名超出范围?

时间:2014-07-28 12:41:02

标签: c# linq

我正在努力更好地理解更复杂的linq语句。感谢我在网上发表的一些文章。我不明白的一件事是为什么我的查询别名在这个陈述中脱离了上下文:

(from query in _context.WebQueries
            select query).Where((from qry in _context.WebQueries
                join qg in _context.WebQueryGroups on qry.QueryKey equals qg.QueryKey
                where qg.QueryGroupNameKey == key
                                              //This is out of scope
                select qry.QueryKey).Contains(query.QueryKey));
                                    //if you replaced it with this same problem
                                    .Contains(qry.QueryKey));

我知道我可以使用匿名对象调用并获得我想要的结果。我将只需要迭代对象并拉出我想要的List:

(from query in _context.WebQueries
         select new {query, key = query.QueryKey})
         .Where(q => !(from qry in _context.WebQueries
                join qg in _context.WebQueryGroups on qry.QueryKey equals qg.QueryKey
                where qg.QueryGroupNameKey == key
                select qry.QueryKey).Contains(q.key));

返回一个对象,其中包含我想要的列表以及稍后要在查询中引用的int。

为什么查询和qry都超出了范围?我宁愿只在我的方法中返回linq语句,而不必解析一个对象来返回列表。讨论这个问题的文章会很棒。

3 个答案:

答案 0 :(得分:7)

我注意到你没有得到你的具体问题的答案,正如我所理解的那样“查询中的范围规则是什么?”

首先,让我们仔细定义“范围”。实体的范围是程序文本的区域,其中特定实体可以由非限定名称引用。

理解范围变量范围的关键在于理解编译器如何翻译查询。这是一个语法翻译。当你说:

from r in s where t select u

由编译器在语法上翻译成:

((s).Where(r => t)).Select(r => u)

在翻译版本中有两个 r s,两个lambda形式参数,以及lambda应用的常规范围规则;每个范围仅适用于lambda的身体。

所以现在你知道为什么你不能在查询之外使用范围变量;范围变量实际上是一个或多个lambda的形式参数,因此仅在那些lambdas的实体中有效。

通过阅读有关查询转换的C#规范部分,您可以了解其余规则。我注意到“透明标识符”查询的规则具有棘手的范围,因此请仔细阅读该部分。我一直想写一篇博客文章。

更新:我开始撰写博客文章;你可以在这里阅读:

http://ericlippert.com/2014/07/31/transparent-identifiers-part-one/

http://ericlippert.com/2014/08/01/transparent-identifiers-part-two/

答案 1 :(得分:3)

虽然有一个答案可以解决潜在问题并使查询起作用,但实际问题尚未得到解答。关于变量超出范围的问题的答案可能会帮助您更好地理解LINQ。

陈述......

from query in _context.WebQueries select query

可以改写为:

_context.WebQueries.Select(query => query)

.Select(query => query)部分是多余的,但为了解释,我把它留在这里)

这个语句可以用一个方法体重写为lambda表达式:

WebQueries.Select(query => { return query; })

(我稍后会解释为什么我不再使用_context.WebQueries

这可以用匿名方法重写为表达式:

WebQueries.Select(delegate(WebQuery query) { return query; })

并且可以使用命名方法将其重写为表达式:

WebQueries.Select(ReturnArg)

其中ReturnArg是这种方法:

WebQuery ReturnArg(WebQuery query)
{
    return query;
}

这是反向顺序的C#历史记录:我们过去只使用命名方法和委托。后来,为了实现LINQ和其他功能,引入了匿名方法和lambda表达式。但需要注意的是,对于编译器而言,方法语法仍然适用,因此lambda表达式query => query只不过是一个具有名为query的参数的方法。与所有方法一样,参数的范围限定为方法主体

在LINQ术语中,此参数称为范围变量,因为它将用作对查询中每个后续元素的引用。

简而言之:范围变量的范围限定在定义它的LINQ语句中。 (from query in _context.WebQueries select query)是一个LINQ语句。随后的Where是一个新的LINQ语句。

我停止使用_context.WebQueries的原因是EF不接受带有方法体的lambda表达式。这是因为方法体静默地将Where方法的参数从表达式转换为Func,而EF仅接受表达式。实际上,整个语句永远不会在CLR中执行,而是转换为SQL并由数据库引擎执行。但是,对于C#编译器,正确性规则仍然适用。范围变量是范围变量,不管它将用于什么。

答案 2 :(得分:1)

我不知道你想要完成什么,但这也是做同样的事情:

(from query in _context.WebQueries
from qry in _context.WebQueries
join qg in _context.WebQueryGroups on qry.QueryKey equals qg.QueryKey
where qg.QueryGroupNameKey == request.Key
where qry.QueryKey == query.QueryKey
select query).Distinct();

然而,这看起来很奇怪。 例如,连接不起任何作用。

为什么不呢?

from query in _context.WebQueries
join qg in _context.WebQueryGroups on query.QueryKey equals qg.QueryKey
where qg.QueryGroupNameKey == request.Key
select query