В чем разница между этими двумя операторами 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.

Другие вопросы по тегам