添加多个包含IQueryables到基础IQueryable更改每个以前的IQueryable

时间:2009-09-30 16:11:01

标签: linq linq-to-sql

我花了很多时间试图弄清楚为什么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框架中的一个错误(好吧,我们知道有时会发生这种情况)。

非常感谢任何帮助。也许我应该以不同的方式向基本查询发送动态查询部分。我对这些想法持开放态度。

1 个答案:

答案 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集合中的最后一个查询的形式。