Почему 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;
Другие вопросы по тегам