使用Linq-to-SQL项目并使用生成的SQL观察一些奇怪的行为。基本上我有一个字符串数组,我需要选择列以其中一个字符串开头的所有行。
using (SqlConnection sqlConn = new SqlConnection(connString))
{
using (IdsSqlDataContext context = new IdsSqlDataContext(sqlConn))
{
//generated results should start with one of these.
//in real code base they are obviously not hardcoded and list is variable length
string[] args = new string[] { "abc", "def", "hig" };
IQueryable<string> queryable = null;
//loop through the array, the first time through create an iqueryable<>, and subsequent passes union results onto original
foreach (string arg in args)
{
if (queryable == null)
{
queryable = context.IdsForms.Where(f => f.MatterNumber.StartsWith(arg)).Select(f => f.MatterNumber);
}
else
{
queryable = queryable.Union(context.IdsForms.Where(f => f.MatterNumber.StartsWith(arg)).Select(f => f.MatterNumber));
}
}
//actually execute the query.
var result = queryable.ToArray();
}
}
我希望生成的sql在功能上等同于以下内容。
select MatterNumber
from IdsForm
where MatterNumber like 'abc%' or MatterNumber like 'def%' or MatterNumber like 'hig%'
但是生成的实际SQL是在下面,注意'hig%'是所有三个like子句的参数。
exec sp_executesql N'SELECT [t4].[MatterNumber]
FROM (
SELECT [t2].[MatterNumber]
FROM (
SELECT [t0].[MatterNumber]
FROM [dbo].[IdsForm] AS [t0]
WHERE [t0].[MatterNumber] LIKE @p0
UNION
SELECT [t1].[MatterNumber]
FROM [dbo].[IdsForm] AS [t1]
WHERE [t1].[MatterNumber] LIKE @p1
) AS [t2]
UNION
SELECT [t3].[MatterNumber]
FROM [dbo].[IdsForm] AS [t3]
WHERE [t3].[MatterNumber] LIKE @p2
) AS [t4]',N'@p0 varchar(4),@p1 varchar(4),@p2 varchar(4)',@p0='hig%',@p1='hig%',@p2='hig%'
答案 0 :(得分:3)
看起来你正在关闭循环变量。这是C#中常见的问题。发生的情况是arg
的值在查询运行时评估,而不是在创建时评估。
创建一个临时变量来保存值:
foreach (string arg in args)
{
var temp = arg;
if (queryable == null)
{
queryable = context.IdsForms.Where(f => f.MatterNumber.StartsWith(temp)).Select(f => f.MatterNumber);
}
else
{
queryable = queryable.Union(context.IdsForms.Where(f => f.MatterNumber.StartsWith(temp)).Select(f => f.MatterNumber));
}
}
您可以阅读关于结束循环变量的this Eric Lippert帖子。正如Eric在文章的顶部所说,正如@Magus在评论中指出的那样,这在C#5中已经发生了变化,因此foreach变量是每次迭代的新副本。如上所述,创建临时变量是向前兼容的。
答案 1 :(得分:1)
联合是正确的,因为你在linq中使用了union来进行sql查询。它们都是hig%
的原因是因为lambda f => f.MatterNumber.StartsWith(arg)
在循环参数周围创建了一个闭包。要修复,请在循环中声明一个局部变量
foreach (string arg in args)
{
var _arg = arg;
if (queryable == null)
{
queryable = context.IdsForms.Where(f => f.MatterNumber.StartsWith(_arg)).Select(f => f.MatterNumber);
}
else
{
queryable = queryable.Union(context.IdsForms.Where(f => f.MatterNumber.StartsWith(_arg)).Select(f => f.MatterNumber));
}
}
但我同意工会似乎没必要。如果要检查的字符串数组不会改变,那么您可以使用标准的where子句。否则你可以看一下谓词构建器! Check here
答案 2 :(得分:0)
这个怎么样?
queryable = context..IdsForms.Where(f =>
{
foreach (var arg in args)
{
if (f.MatterNumber.StartsWith(arg))
return true;
}
return false;
}).Select(f => f.MatterNumber);