Entity Framework Сначала выберите новый POCO без .ToList () - PullRequest
14 голосов
/ 03 марта 2011

Я создаю приложение с сервисным уровнем (веб-сайт WCF) и клиентом Silverlight 4. Услуги RIA не являются опцией, поэтому мы создаем промежуточные классы для передачи туда и обратно. Для целей этого вопроса давайте предположим, что я передаю туда и обратно Вкусные Food Объекты.

public class FoodData
{
  public int Id { get; set; }
  public string Name { get; set; }
  public Tastyness TastyLevel { get; set; }
}

Модель EF - это, по сути, один и тот же класс, таблица с тремя основными полями (Tastyness - это int, соответствующий нашему enum Tastyness).

Я часто использую подобные заявления при выполнении запросов Entity Framework:

public List<FoodData> GetDeliciousFoods()
{
  var deliciousFoods = entities.Foods
                               .Where(f => f.Tastyness == (int)Tastyness.Delicious)
                               .ToList()  // Necessary? And if so, best performance with List, Array, other?
                               .Select(dFood => dFood.ToFoodData())
                               .ToList();

  return deliciousFoods;
}

Без вызова .ToList () я получаю исключение из-за того, что LINQ не может преобразовать пользовательский метод в эквивалент запроса, что, как я понимаю.

Мой вопрос касается вызова .ToList () перед .Select (...) с пользовательским расширением для преобразования нашего объекта в POCO-версию Food объект.

Есть ли лучший шаблон для выполнения здесь или, может быть, даже лучшая альтернатива .ToList (), которая может быть более производительной, поскольку мне не требуется функциональность результата List <..>.

Ответы [ 4 ]

11 голосов
/ 04 марта 2011

Проблема с использованием ToList или AsEnumerable заключается в том, что вы материализуете всю сущность и оплачиваете стоимость исправления.Если вы хотите иметь наилучший возможный SQL, который возвращает только необходимые поля, вам следует проецировать напрямую, а не с помощью .ToFoodData():

var deliciousFoods = entities.Foods
                             .Where(f => f.Tastyness == (int)Tastyness.Delicious)
                             .Select(dFood => new FoodData
                                  {
                                      Id = dFood.Id,
                                      Name = dFood.Name,
                                      TastyLevel = (Tastyness)dFood.Tastyness
                                  });

Приведение к enum может быть проблемой.Если это так, перейдите к анонимному типу:

var deliciousFoods = entities.Foods
                             .Where(f => f.Tastyness == (int)Tastyness.Delicious)
                             .Select(dFood => new FoodData
                                  {
                                      Id = dFood.Id,
                                      Name = dFood.Name,
                                      TastyLevel = dFood.Tastyness
                                  })
                             .AsEnumerable()
                             .Select(dFood => new FoodData
                                  {
                                      Id = dFood.Id,
                                      Name = dFood.Name,
                                      TastyLevel = (Tastyness)dFood.TastyLevel
                                  });

Если вы изучите полученный SQL, вы увидите, что он проще, и вы не заплатите за исправление объектов в ObjectContext.

6 голосов
/ 03 марта 2011

Используйте AsEnumerable(), чтобы превратить запрос в обычный запрос LINQ to Objects без необходимости создавать ненужный список

var deliciousFoods = entities.Foods
                               .Where(f => f.Tastyness == (int)Tastyness.Delicious)
                               .AsEnumerable()
                               .Select(dFood => dFood.ToFoodData())
                               .ToList();

Редактировать: см. http://www.hookedonlinq.com/AsEnumerableOperator.ashx

1 голос
/ 22 октября 2015

Ответ @Craig Stuntz правильный, однако может возникнуть проблема, когда у вас есть несколько запросов, которые должны преобразовать объект «Food» в объект «FoodData». Вы не хотите, чтобы выражение дублировалось в нескольких местах (СУХОЙ).

Решение может заключаться в том, чтобы не иметь метод, который фактически возвращает объект «FoodData», а иметь метод, который возвращает выражение, которое будет использоваться для преобразования. Затем вы можете повторно использовать этот метод.

Class Food {
  ...

  public static Expression<Func<Food, FoodData> ConvertToFoodDataExpr() {
    Expression<Func<Food, FoodData>> expr = dFood => new FoodData 
    {
      Id = dFood.Id,
      Name = dFood.Name,
      TastyLevel = dFood.Tastyness
    }
  }
}

И использовать это ...

var deliciousFoods = entities.Foods
                         .Where(f => f.Tastyness == (int)Tastyness.Delicious)
                         .Select(Food.ConvertToFoodDataExpr());

Помните, что при использовании Entity Framework не следует материализовать IEnumerable (используя ToList, ToArray и т. Д.) Для применения выражения select, в противном случае Entity Framework не сможет сделать правильный оператор выбора и всегда будет выбирать все поля из таблицы.

0 голосов
/ 03 марта 2011

Первый .ToList () не требуется.

var deliciousFoods = entities.Food

    // Here a lazy-evaluated collection is created (ie, the actual database query
    // has not been run yet)
    .Where(f => f.Tastyness == (int)Tastyness.Delicious)

    // With ToArray, the query is executed and results returned and 
    // instances of Food created.  The database connection
    // can safely be closed at this point.
    // Given the rest of the linq query, this step can be skipped
    // with no performance penalty that I can think of
    .ToArray()

    // Project result set onto new collection.  DB Query executed if
    // not already
    // The above .ToArray() should make no difference here other
    // than an extra function call an iteration over the result set
    .Select(dFood => dFood.ToFoodData())

    // This one might not be needed, see below
    .ToList();

Требуется ли, чтобы набор результатов был List <>?Или будет достаточно IEnumerable или ICollection?Если так, то последний .ToList () может не понадобиться.

Вы спрашивали о производительности?Сколько экземпляров вы ожидаете получить за запрос?Если это относительно немного, то .ToList () или .ToArray (), или другие не имеют никакого значимого различия.Это больше о том, какую функциональность вам нужно выставить?Если возвращаемый объект должен быть индексируемым, добавляемым и иметь другие свойства List, это нормально.Но если все, что вы делаете, это перебираете возвращенную коллекцию, не раскрывайте то, что не нужно.

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