Разница между cast и as внутри 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();


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

820   3  

3 ответов:

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

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

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

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

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

  2. (Foo)x is not translated into SQL. EF знает, что нет способа создать Foo объекты для все x's, как требует семантика приведения, и это вызывает исключение.

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

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

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

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

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

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

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

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

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

Comments

    Ничего не найдено.