В чем разница между этими двумя операторами LINQtoSQL?
Эти два утверждения логически выглядят одинаково для меня, но в результате они генерируют разные 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 работает.
Сгенерированный SQL для var people
запрос идентичен для обоих, но 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, - это проверка запрашиваемого в отладчике. Однако после настройки логгера, как предположил Джон, кажется, что реальный 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]
2 ответа
Во-первых, подумайте о двойственной природе e Enum:
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" в качестве допустимых значений в логике.
Разница в том, что внутреннее представление enum в CLR является целочисленным, а в первом примере параметр @p передается как целое число, это поведение построителя запросов L2S, поэтому преобразование.
Первый из них сработал бы, если бы в моем примере столбец базы данных представлял собой int, который принимает значения, назначенные членам Enum {1,2,10}.
Нет, они разные. В первой версии выражение MyPersonEnum.STUDENT.ToString()
находится внутри дерева выражений - это часть того, что LINQ to SQL должен преобразовать в SQL. Мне было бы интересно посмотреть, что @p0
когда запрос выполняется...
Во второй версии вы уже оценили выражение, поэтому LINQ to SQL просто видит ссылку на переменную, которая уже является строкой.
Мы знаем, что они означают одно и то же, но, по-видимому, LINQ to SQL не обладает достаточными знаниями, чтобы понять это.
Из интереса, они оба работают?
РЕДАКТИРОВАТЬ: Хорошо, так что вторая версия работает. Я предлагаю вам использовать эту форму тогда:) В идеальном мире оба будут работать - но в этом случае, похоже, вам нужно немного помочь LINQ to SQL.