这两个LINQtoSQL语句有什么区别?

5

在逻辑上,这两个语句看起来相同,但它们生成的SQL不同:

#1 
var people = _DB.People.Where(p => p.Status == MyPersonEnum.STUDENT.ToString());
var ids = people.Select(p => p.Id);
var cars = _DB.Cars.Where(c => ids.Contains(c.PersonId));

#2 
string s = MyPersonEnum.STUDENT.ToString();
var people = _DB.People.Where(p => p.Status == s);
var ids = people.Select(p => p.Id);
var cars = _DB.Cars.Where(c => ids.Contains(c.PersonId));

示例1无法工作,但示例2可以。

对于var people查询生成的SQL相同,但是最终查询中的SQL不同,如下所示:

#1
SELECT [t0].[PersonId], [t0].[etc].....
FROM [Cars] AS [t0]
WHERE EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [People] AS [t1]
    WHERE ([t1].[Id] = [t0].[PersonId]) AND ([t1].[Status] = (CONVERT(NVarChar,@p0)))
    )

#2
SELECT [t0].[PersonId], [t0].[etc].....
FROM [Cars] AS [t0]
WHERE EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [People] AS [t1]
    WHERE ([t1].[Id] = [t0].[PersonId]) AND ([t1].[Status] = @p0)
    )

为什么会有这种差异?

编辑:

到目前为止,我所做的一切都是在调试器中检查可查询内容以生成SQL。然而,在像Jon建议的设置记录器之后,似乎执行的真正SQL不同。

#1 
SELECT [t1].[Id], [t1].etc ... [t0].Id, [t1].etc ...
FROM [Cars] AS [t0], [People] AS [t1]
WHERE ([t1].[Id] = [t0].[PersonId]) AND (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [People] AS [t2]
    WHERE ([t2].[Id] = [t0].[PersonId]) AND ([t2].[Status] = (CONVERT(NVarChar,@p0)))
    )) AND ([t1].[Status] = @p1)
-- @p0: Input Int (Size = 0; Prec = 0; Scale = 0) [2]
-- @p1: Input NVarChar (Size = 7; Prec = 0; Scale = 0) [STUDENT]

#2
SELECT [t1].[Id], [t1].etc ... [t0].Id, [t1].etc ...
FROM [Cars] AS [t0], [People] AS [t1]
WHERE ([t1].[Id] = [t0].[PersonId]) AND (EXISTS(
    SELECT NULL AS [EMPTY]
    FROM [People] AS [t2]
    WHERE ([t2].[Id] = [t0].[PersonId]) AND ([t2].[Status] = @p0)
    )) AND ([t1].[Status] = @p1)
-- @p0: Input NVarChar (Size = 7; Prec = 0; Scale = 0) [STUDENT]
-- @p1: Input NVarChar (Size = 7; Prec = 0; Scale = 0) [STUDENT]

如果您尝试使用字符串s = MyPersonenum.STUDENT.ToString();,它是否有效? - Matt Ellen
是的,那实际上是我在那里使用的代码,只是出于习惯打了 var,我会编辑我的帖子。 - Kirschstein
2个回答

1

首先,考虑枚举类型的双重性质:

enum MyPersonEnum
{
  STUDENT, // implicit 1
  TEACHER, // implicit 2
  DIRECTOR = 10 // explicit 10
}

...

Assert.AreEqual(1, (int)MyPersonEnum.STUDENT);
Assert.AreEqual("STUDENT", MyPersonEnum.STUDENT.ToString());

在第二个示例中,C#已将Enum转换为字符串,因此不需要转换,并且假定您的数据库People.Status列接受“STUDENT”、“TEACHER”、“DIRECTOR”字符串作为逻辑上的有效值。
区别在于CLR中的枚举内部表示为整数,在第一个示例中,@p参数作为整数传递,这是L2S查询构建器的行为,这就是为什么需要转换。
如果您的数据库列是int类型且在我的示例中采用了Enum成员{1,2,10}分配的值,则第一个方法可以工作。

1

不,它们是不同的。在第一个版本中,表达式MyPersonEnum.STUDENT.ToString()在表达式树中 - 它是LINQ to SQL必须转换为SQL的一部分。当查询被执行时,我很想知道@p0是什么...

在第二个版本中,您已经评估了表达式,因此LINQ to SQL只看到对已经是字符串的变量的引用。

我们知道它们意思相同,但是可能LINQ to SQL没有足够的知识来理解这一点。

出于兴趣,它们都有效吗?

编辑:好的,第二个版本有效。我建议您使用那种形式 :) 在理想的情况下,两者都应该有效 - 但在这种情况下,似乎您需要帮助LINQ to SQL一下。


@DTashkinov:在第一个版本中,它确实构建了一个表达式树。 - Jon Skeet
我使用能够正常工作的版本就可以了,只是出于好奇想知道问题出在哪里而已 ;) 我该如何检查查询执行时 @p0 的值是多少? - Kirschstein
@Kirschstein:设置数据上下文的日志属性,它将显示查询和参数值。 - Jon Skeet
@leppie:你不认为它会到达那里吗? - Jon Skeet
@Kirschstein:大体上是对的。基本上,LINQ to SQL将MyPersonEnum.STUDENT.ToString()解释为int.ToString()转换——这有点出毛病了,坦率地说。 - Jon Skeet
显示剩余5条评论

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接