Разница между литой и внутри внутри select в LINQ

Этот код генерирует исключение:

var query = services
             .SomeQuery(bar).select(x => (Foo)x)
             .Where(x.PropertyOfFoo == FooState.SomeState);
var result = query.ToList(); 

Исключение:

Unable to cast the type...
LINQ to Entities only supports casting EDM primitive or enumeration types.

Этот код работает:

var query = services
             .SomeQuery(bar).select(x => x as Foo)
             .Where(x.PropertyOfFoo == FooState.SomeState);
var result = query.ToList(); 

Почему as разрешить преобразование и cast нет?

Я понимаю, что as будет возвращать значение null, а cast будет генерировать исключение, если любой из вызовов завершится с ошибкой. Хорошо. Но когда я запускаю этот код:

var query = services
             .SomeQuery(bar);
var result = query.ToList(); 

Я получаю гораздо больший результат запроса. Почему?

Ответ 1

LINQ to Entities - это не то же самое, что LINQ to Objects. Хотя Функции LINQ to Objects могут принимать любой соответствующий делегат и вслепую вызывать его как обычный код С#, LINQ to Entities обрабатывает ваши lambdas как деревья выражений, поскольку он должен понимать семантику этих lambdas (а не только их подпись) и преобразовывать их в эквивалентный запрос для вашего EF-сервера. Естественно, это означает, что LINQ to Entities не может обрабатывать все операции, которые могут быть связаны с LINQ to Objects.

Теперь, как вы сказали, одна разница между кастингом и использованием as заключается в том, что casting генерирует исключение при ошибке и as возвращает null. Но существует еще более важное различие между ними: приведение будет применять любые потенциальные пользовательские явные преобразования, тогда как as просто попытается переосмыслить ссылку как нечто иначе, игнорируя любые потенциальные преобразования.

Как вы понимаете, бросок гораздо сложнее, чем as, потому что приведение может вызвать настраиваемый метод (explicit operator), который нелегко разрешить поставщиком LINQ. Вероятно, провайдер просто не может проверить код потенциального пользовательского преобразования (я недостаточно знаю об ограничениях деревьев выражений), не говоря уже о переводе его для основного источника. Таким образом, LINQ to Entities позволяет разрешать только as и простейшие случаи литья (в основном, это позволяет случаи, когда логика преобразования была известна заранее и не может быть пользовательским кодом пользователя).

Ответ 2

Там важная разница между двумя утверждениями, которые показывают, что здесь больше всего участвует (например, правила CLR) Entity Framework.

  • x as Foo преобразуется в SQL. EF может это сделать, потому что он знает, что он всегда будет возвращать результат, либо Foo, либо null. Сгенерированный SQL, кстати, чудовищный, но он выполняет задание.

  • (Foo)x не переведен в SQL. EF знает, что нет способа создать объекты Foo для всех x, так как требует семантика, и она генерирует исключение.

Обратите внимание, что EF примет foos.Select(f => (Bar)f), потому что всегда можно создать базовый тип из подтипа. Фактический отбор выполняется после получения результата SQL, он не влияет на сам SQL.

Он также примет bars.OfType<Foo>().Select(x => (Foo)x) (хотя это довольно бесполезно).

Итак, сообщение об ошибке...

LINQ to Entities поддерживает только листинг примитивных или перечисляемых типов EDM.

... не совсем верно. EF действительно принимает отливки, когда они выполнимы. Таким образом, внутри переводчика EF-запросов есть всего лишь куча логики, чтобы решить, стоит ли пытаться генерировать инструкцию SQL.

Ответ 3

Различие в том, как работает прямой бросок, как работает оператор as. Прямая трансляция недоступна, потому что вы не используете тип значения - она ​​работает только для примитивов.

Теперь ваша лямбда пытается выбрать и спроектировать - вы можете разбить это на две операции, поэтому:

var result = services.SomeQuery(bar).select(x => new Foo() {
    SomeProperty = x.SomeProperty,
    SomeOtherProperty = x.SomeOtherProperty,
    ... }).ToList()

Что касается большего количества результатов: вам не хватает предложения where.