Почему 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 отказывается. Является ли эта настройка отсутствующей или просто ограничивает ограничения между этими двумя? Почему это происходит?

Ответ 1

Проблема вызвана типом, возвращаемым вашим репозиторием; проблема может быть воспроизведена, если _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.

Ответ 2

Просто ради любопытства, попробовали ли вы конвертировать

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;