Может ли LINQ To SQL генерировать неверный SQL?

У меня есть две таблицы, с которыми я использую Linq to SQL. Таблицы имеют связь 1 ко многим. Важная часть схемы базы данных выглядит следующим образом:

Camera:
  Id (int)
  SerialNumber (string)
  ...

CameraCalibration
  Id (int)
  CameraFk (int)
  ...

Используя LINQ to SQL, я могу получить список всех калибровок камер для камер с 10-символьными серийными номерами следующим образом.

var query = from n in db.CameraCalibrations
    where n.Camera.SerialNumber.Length == 10
    select n

Насколько я понимаю LINQ, следующий запрос должен работать одинаково (даже если сравнивать его довольно жестоко...)

var query = from n in db.CameraCalibrations
where db.Cameras.Where(c => c.Id == n.CameraFk).SingleOrDefault()
                .SerialNumber.Length == 10
select n

Однако, когда я выполняю этот второй запрос к базе данных, я получаю исключение SQL, в котором говорится: «Невозможно вызвать методы для nvarchar». Когда я смотрю на сгенерированный SQL, становится совершенно ясно, почему генерируется исключение:

SELECT t0.*
FROM CameraCalibration AS t0
WHERE (
    SELECT t1.serialNumber.Length
    FROM Camera AS t1
    WHERE t1.id = t0.cameraFk
    ) = 10

Обратите внимание, как сгенерированный SQL использует выражение t1.serialNumber.Length? Я ожидал, что это будет переведено в LEN(t1.serialNumber), и действительно, с этим изменением запрос работает.

Я делаю что-то не так здесь? Или это ошибка в том, как я структурировал свой запрос и/или ограничение LINQ to SQL?

Хотя я могу легко реструктурировать запрос, который использовал для демонстрации проблемы, в моем реальном сценарии это будет намного сложнее (частично из-за задействования динамически генерируемого LINQ).

Как я могу написать запрос, аналогичный второму (с поиском, выполняемым в предложении where), который LINQ to SQL будет счастлив выполнить?


person Christopher Fairbairn    schedule 21.01.2010    source источник
comment
Довольно странно - похоже, вы нашли ошибку. Тем не менее, почти всегда лучше использовать соединение, чем подзапрос, поэтому я бы выбрал ответ Роберта Харви.   -  person Aaronaught    schedule 21.01.2010


Ответы (1)


В этом конкретном случае, я думаю, вам нужно объединить две таблицы в своем заявлении Linq. Это позволит вам получить экземпляр SerialNumber без использования ассоциации .SerialNumber.Length. Что-то вроде этого (не проверено):

var query = from n in db.CameraCalibrations 
            join c in db.Cameras on c.Id equals n.CameraFk
            where c.SerialNumber.Length == 10
            select n;
person Robert Harvey    schedule 21.01.2010
comment
Ваше решение работает. Другой способ — использовать db.Cameras.Where(c => c.Id == n.CameraFk).SingleOrDefault(z => z.SerialNumber.Length == 10) != null в качестве предложения where. Моя проблема заключается в том, что в моем реальном сценарии структура, динамически генерирующая запросы LINQ, не позволит мне легко переписать запросы для использования соединения и т. д. Поэтому я пытаюсь понять, почему запросы существующей структуры вызывают исключения SQL. - person Christopher Fairbairn; 21.01.2010