我花了很多时间试图弄清楚为什么Linq2SQL正在改变我的SQL查询。这很难解释,我找不到任何理由发生这种情况。低位似乎是在IQueryable附近添加多个包含似乎会覆盖每个先前的IQueryable表达式。让我试着解释一下:
假设您有一个Linq2SQL查询,它为您提供查询的基本框架。 (这是所有查询的基础)
我动态添加了where查询的部分内容(在下面的示例中显示为“partQuery”)。从where查询生成的Expression是正确的,当我将它添加到finalQuery时 - 它仍然是正确的。当我向最终查询添加另一个partQuery时,问题出现了,它似乎覆盖了最终查询中的第一个查询,但是在其中添加了第二个查询。 (或如下所示,添加第三个查询时,覆盖前两个查询)
以下是一些来源示例:
foreach (var partQuery in whereStatements)
{
finalQuery = finalQuery.Where(
dataEvent => partQuery.Contains(dataEvent.DataEventID)
);
}
partQuery的类型为IQueryable finalQuery是最终将在SQL服务器上执行的查询
// the list of the wheres that are sent
var whereStatements = new List<IQueryable<long>>();
var query1 = DataEvent.GetQueryBase(context);
query1 = query1.Where(
dataEvent =>
dataEvent.DataEventKeyID == (short)DataEventTypesEnum.TotalDollarAmount && dataEvent.ValueDouble < -50);
whereStatements.Add(query1.Select(x => x.DataEventID));
var query2 = DataEvent.GetQueryBase(context);
query2 = query2.Where(
dataEvent =>
dataEvent.DataEventKeyID == (short)DataEventTypesEnum.ObjectNumber && dataEvent.ValueDouble == 6);
whereStatements.Add(query2.Select(x => x.DataEventID));
Query(query1)的第一个表达式如下所示:
{SELECT [t0].[DataEventID]
FROM [dbo].[DataEvents] AS [t0]
INNER JOIN [dbo].[DataEventAttributes] AS [t1] ON [t0].[DataEventID] = [t1].[DataEventID]
WHERE ([t1].[DataEventKeyID] = @p0) AND ([t1].[ValueDouble] < @p1)
}
请注意,where行有一个ValueDouble“&lt;” @ p1 - 少于
然后当添加到最终查询中时,它看起来像这样:
{SELECT [t0].[DataEventID], [t0].[DataOwnerID], [t0].[DataTimeStamp]
FROM [dbo].[DataEvents] AS [t0]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t1]
INNER JOIN [dbo].[DataEventAttributes] AS [t2] ON [t1].[DataEventID] = [t2].[DataEventID]
WHERE ([t1].[DataEventID] = [t0].[DataEventID]) AND ([t2].[DataEventKeyID] = @p0) AND ([t2].[ValueDouble] < @p1)
)) AND ([t0].[DataOwnerID] = @p2)
}
此时,查询仍然正确。请注意ValueDouble如何仍然具有“&lt;”标志。当我向查询添加2个或更多时,会出现问题。以下是此示例中第二个查询的表达式:
{SELECT [t0].[DataEventID]
FROM [dbo].[DataEvents] AS [t0]
INNER JOIN [dbo].[DataEventAttributes] AS [t1] ON [t0].[DataEventID] = [t1].[DataEventID]
WHERE ([t1].[DataEventKeyID] = @p0) AND ([t1].[ValueDouble] = @p1)
}
并且当添加到最终查询中时......您会注意到第一个查询不再正确....(还有更多信息)
{SELECT [t0].[DataEventID], [t0].[DataOwnerID], [t0].[DataTimeStamp]
FROM [dbo].[DataEvents] AS [t0]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t1]
INNER JOIN [dbo].[DataEventAttributes] AS [t2] ON [t1].[DataEventID] = [t2].[DataEventID]
WHERE ([t1].[DataEventID] = [t0].[DataEventID]) AND ([t2].[DataEventKeyID] = @p0) AND ([t2].[ValueDouble] = @p1)
)) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t3]
INNER JOIN [dbo].[DataEventAttributes] AS [t4] ON [t3].[DataEventID] = [t4].[DataEventID]
WHERE ([t3].[DataEventID] = [t0].[DataEventID]) AND ([t4].[DataEventKeyID] = @p2) AND ([t4].[ValueDouble] = @p3)
)) AND ([t0].[DataOwnerID] = @p4)
}
还有一个好处......在通过SQL分析器查看之后,它似乎完全删除了第一个查询,并且最终SQl中的两个Exists子句实际上是相同的查询(query2)。对于第一个查询,没有任何参数实际传递给SQl服务器。
因此,在我对此的研究中,似乎它向SQl添加了查询,但它将所有现存的where子句替换为添加的最后一个子句。要对此进行双重确认..与上面完全相同的代码,但我添加了第三个查询....并查看它是如何更改的。
var query3 = DataEvent.GetQueryBase(context);
query3 = query3.Where(
dataEvent =>
dataEvent.DataEventKeyID != (short)DataEventTypesEnum.Quantity && dataEvent.ValueDouble != 5);
whereStatements.Add(query3.Select(x => x.DataEventID));
我把一些“!=”扔到了查询的最后一部分
{SELECT [t0].[DataEventID], [t0].[DataOwnerID], [t0].[DataTimeStamp]
FROM [dbo].[DataEvents] AS [t0]
WHERE (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t1]
INNER JOIN [dbo].[DataEventAttributes] AS [t2] ON [t1].[DataEventID] = [t2].[DataEventID]
WHERE ([t1].[DataEventID] = [t0].[DataEventID]) AND ([t2].[DataEventKeyID] <> @p0) AND ([t2].[ValueDouble] <> @p1)
)) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t3]
INNER JOIN [dbo].[DataEventAttributes] AS [t4] ON [t3].[DataEventID] = [t4].[DataEventID]
WHERE ([t3].[DataEventID] = [t0].[DataEventID]) AND ([t4].[DataEventKeyID] <> @p2) AND ([t4].[ValueDouble] <> @p3)
)) AND (EXISTS(
SELECT NULL AS [EMPTY]
FROM [dbo].[DataEvents] AS [t5]
INNER JOIN [dbo].[DataEventAttributes] AS [t6] ON [t5].[DataEventID] = [t6].[DataEventID]
WHERE ([t5].[DataEventID] = [t0].[DataEventID]) AND ([t6].[DataEventKeyID] <> @p4) AND ([t6].[ValueDouble] <> @p5)
)) AND ([t0].[DataOwnerID] = @p6)
}
注意所有三个内部查询现在都是“&lt;&gt;”上面的查询不是那个。
我完全不在这里吗?我错过了这么简单的事情吗?当你告诉我我想要拉掉我的指甲时?我实际上希望你告诉我,而不是告诉我它看起来像MS框架中的一个错误(好吧,我们知道有时会发生这种情况)。
非常感谢任何帮助。也许我应该以不同的方式向基本查询发送动态查询部分。我对这些想法持开放态度。
答案 0 :(得分:3)
如果没有对您的示例进行全面评估,那么最重要的一点是:
foreach (var partQuery in whereStatements)
{
finalQuery = finalQuery.Where(
dataEvent => partQuery.Contains(dataEvent.DataEventID)
);
}
由于此循环的结构方式,每次迭代中生成的每个表达式最终都将使用partQuery
的最终值 - 循环终止时存在的值。你可能想要这个,而不是:
foreach (var partQuery in whereStatements)
{
var part = partQuery;
finalQuery = finalQuery.Where(
dataEvent => part.Contains(dataEvent.DataEventID)
);
}
现在,part
是捕获的变量,每次迭代都是唯一的,因此每个表达式都是唯一的。这种奇怪的第一种行为是设计的:见a related question。
编辑:看起来这正是造成你问题的原因;最终查询中的子查询都是x <> y
形式,这是添加到whereStatements
集合中的最后一个查询的形式。