在foreach / for中构建where子句的延迟查询

时间:2012-04-19 19:21:34

标签: c# linq entity-framework entity-framework-4

在对错误进行故障排除时遇到了这个问题。这似乎是由于延迟执行而查询使用的是变量关键字的最后一个值。建议的解决方案是什么。

var query = from c in dbContext.Cars
            select c;

var keywords = new string[] { "Clean", "Car" };

foreach (var keyword in keywords)
{
    query = query.Where(c => c.Name.Contains(keyword));
}

var cars = query.ToList();

这样的查询结果(注意两个参数的值都是“Car”)

exec sp_executesql N'SELECT 
[Extent1].[CarID] AS [MID], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[Cars] AS [Extent1]
WHERE ([Extent1].[Name] LIKE @p__linq__0 ESCAPE N''~'') AND ([Extent1].[Name] LIKE @p__linq__1 ESCAPE N''~'')
',N'@p__linq__0 nvarchar(4000),@p__linq__1 nvarchar(4000)',@p__linq__0=N'%Car%',@p__linq__1=N'%Car%'

LINQPad中的调试也显示相同的查询

DECLARE p__linq__0 NVarChar(1) = '%Car%'
DECLARE p__linq__1 NVarChar(1) = '%Car%'

SELECT 
[Extent1].[CarID] AS [MID], 
[Extent1].[Name] AS [Name], 
FROM [dbo].[Cars] AS [Extent1]
WHERE ([Extent1].[Name] LIKE @p__linq__0 ESCAPE N'~') AND ([Extent1].[Name] LIKE @p__linq__1 ESCAPE N'~')

1 个答案:

答案 0 :(得分:5)

这是由于匿名函数中捕获变量的方式。改变这个:

foreach (var keyword in keywords)
{
    query = query.Where(c => c.Name.Contains(keyword));
}

到此:

foreach (var keyword in keywords)
{
    var copy = keyword;
    query = query.Where(c => c.Name.Contains(copy));
}

它应该有效。有关详细信息,请参阅Eric Lippert's blog post。 C#5的行为正在改变,所以你不必担心这一点。

但是,请注意,您也可以尝试让LINQ为您执行此操作:

var query = dbContext.Cars.Where(c => keywords.All(key => c.Name.Contains(key));

我很确定这适用于Any(有效地“或”),但我不确定它是否适用于All。试一试......