Entity Framework и дерево лямбда-выражений (глубокое объединение нулей) - PullRequest
4 голосов
/ 04 декабря 2011
var articles = context.Articles.Where(a => a.Id != articleId)
.OrderBy(p => p.Categories.OrderBy(q => q.Name).FirstOrDefault().Name).ToList();

я получаю сообщение о возможном исключении NullReferenceException, которое является правильным.

Так что я делаю

var  articles = context.Articles.Where(a => a.Id != articleId)
                               .OrderBy(p =>
                                   (p.Categories.OrderBy(q => q.Name).FirstOrDefault() != null
                                    ? p.Categories.OrderBy(q => q.Name).FirstOrDefault().Name
                                    : null))
                               .Skip(page * pageSize)
                                  .Take(pageSize)
                                  .ToList();

, который работает, но оператор вызывает два раза и может быть медленным, поэтому я пытаюсь сделать

var articles = context.Articles.Where(a => a.Id != articleId)
             .OrderBy(p =>
             {
                 var firstOrDefault = p.Categories.OrderBy(q => q.Name).FirstOrDefault();
                 return firstOrDefault != null ? firstOrDefault.Name : null;
             }).ToList();

, но я получаю

лямбда-выражение с телом оператора не может быть преобразовано в дерево выражений.

Что я могу сделать?Первый пример правильный, даже если я позвоню два раза p.Categories.OrderBy(q => q.Name).FirstOrDefault().

Я думаю, что это может быть медленно.У меня есть 200 тыс. Строк в базе данных.

Ответы [ 3 ]

4 голосов
/ 04 декабря 2011

Я получаю сообщение о возможном исключении NullReferenceException, которое является правильным.

Не ясно, какая система создает это сообщение. Resharper?

В любом случае, в этом случае, если это действительно LINQ to Entities, предупреждение является ложным. LINQ to Entities во многих случаях выполняет автоматическое «глубокое слияние нулей», и это один из таких случаев.

В вашем исходном запросе:

var articles = context.Articles
                      .Where(a => a.Id != articleId)
                      .OrderBy(p => p.Categories
                                     .OrderBy(q => q.Name)
                                     .FirstOrDefault()
                                     .Name)
                       .ToList();

... не будет NullReferenceException, если у статьи нет связанной с ней категории. Вместо этого значение заказа будет приниматься за null для таких статей (то есть, статьи без категорий будут появляться первыми), что, по-видимому, именно то, что вам нужно. Так что никаких дополнительных усилий с вашей стороны не требуется!

Обратите внимание, что с другими поставщиками LINQ (такими как LINQ to Objects) поведение может быть совершенно другим, и .FirstOrDefault().XXX действительно является рискованным выражением.

С другой стороны, не оптимизируйте преждевременно. Если у вас уже есть рабочее решение, сравните его. Если это слишком медленно, выясните , почему - в этом случае ключи находятся в сгенерированном SQL. Оптимизатор запросов LINQ to Entities часто бывает умнее, чем вы думаете. :)

2 голосов
/ 04 декабря 2011

Ваш второй пример действительно произведет гораздо более сложный запрос SQL, но это зависит от базы данных, если этот запрос будет действительно медленнее.В любом случае, вы можете просто немного изменить свой первый запрос, и он должен работать, как и ожидалось, без этого предупреждения:

var articles = context.Articles
                      .Where(a => a.Id != articleId)
                      .OrderBy(p => p.Categories
                                     .OrderBy(q => q.Name)
                                     .Select(q => q.Name)
                                     .FirstOrDefault())
                      .ToList();

Проблема в вашем запросе заключается в выборе Name после вызова FirstOrDefault, поэтому, если вы проецируетерезультат до вызова FirstOrDefault не должен выдавать предупреждение, но в результирующем SQL будет дополнительный подвыбор.

Кстати.@ Ани ответ правильный.

0 голосов
/ 04 декабря 2011

Похоже, что это повторяющийся ответ: «Лямбда-выражение с телом оператора не может быть преобразовано в дерево выражений» Может показаться, что вы не можете использовать блок кода, обозначенный фигурным фигурные скобки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...