Почему EF 5.0 не поддерживает этот синтаксис EF 4.x LINQ при компиляции в sql?
У меня есть код, который был недавно обновлен с EF 4.2 до EF 5.0 (фактически EF 4.4, так как я работаю на.Net 4.0). Я обнаружил, что мне пришлось изменить синтаксис моего запроса, и мне любопытно, почему. Позвольте мне начать с проблемы.
У меня есть таблица EventLog, которая периодически заполняется клиентом. Для каждого журнала событий создается запись в таблице отчетов. Этот запрос периодически запускается для обнаружения любых журналов событий, у которых еще нет записи в таблице отчетов. Запрос, который я использовал в EF 4.2, был:
from el in _repository.EventLogs
where !_repository.Reports.Any(p => p.EventLogID == el.EventlogID)
После обновления до EF 5.0 я получаю следующую ошибку во время выполнения:
System.NotSupportedException: невозможно создать постоянное значение типа "Namespace.Report". В этом контексте поддерживаются только примитивные типы или типы перечисления.
Я обнаружил, что переписывание его с помощью синтаксиса соединения исправило проблему. Следующее работает в EF 5.0 и примерно эквивалентно:
from eventLog in _repository.EventLogs
join report in _repository.Reports on eventLog.EventlogID equals report.EventLogID into alreadyReported
where !alreadyReported.Any()
Некоторые люди могут иметь разные мнения о смешанном синтаксисе / стиле первого запроса, но меня действительно больше интересует причина этого. Кажется странным, что компилятор EF 4.2 мог генерировать SQL для исходного запроса, но EF 5.0 отказывается. Я пропускаю этот параметр или просто ужесточаю ограничения между ними? Почему это происходит?
2 ответа
Проблема вызвана типом, возвращаемым вашим хранилищем; проблема может быть воспроизведена, когда _repository.Reports
не является IQueryable<T>
, В этом случае Reports
рассматривается как нескалярная переменная; что, кстати, не разрешено в LINQ. См. Ссылки на нескалярные переменные, которые не поддерживаются.
На ваш вопрос о том, почему работает второй запрос, это в основном следующий метод расширения IQueryable<T>
какая группа присоединяется к IEnumerable<TInner>
,
public static IQueryable<TResult> GroupJoin<TOuter, TInner, TKey, TResult>(
this IQueryable<TOuter> outer,IEnumerable<TInner> inner,
Expression<Func<TOuter, TKey>> outerKeySelector,
Expression<Func<TInner, TKey>> innerKeySelector,
Expression<Func<TOuter, IEnumerable<TInner>, TResult>> resultSelector)
Который просто принимает выражение для ключевых селекторов как для внешних, так и для внутренних (вместо ссылки на не скалярные переменные); в котором вышеуказанное ограничение не применяется.
Примечание: если _repository.Reports
имеет IQueryable<T>
первый запрос будет работать; потому что EF правильно построит дерево выражений и выполнит соответствующий SQL.
Ради любопытства, вы пробовали конвертировать?
from el in _repository.EventLogs
where !_repository.Reports.Any(p => p.EventLogID == el.EventlogID)
в
from el in _repository.EventLogs
where !_repository.Reports.Where(p => p.EventLogID == el.EventlogID).Any();
или же
from el in _repository.EventLogs
where !_repository.Reports.Where(p => p.EventLogID == el.EventlogID).Count() > 0;