Почему это выражение LINQ нарушает мой цикл и логику преобразования? - PullRequest
3 голосов
/ 26 января 2012

Фон

ArticleService - это класс, предоставляющий методы для интерфейсных слоев для облегчения работы с серверной частью.

Двумя основными обязанностями является преобразование ViewModels (ArticleViewModel) в соответствующие модели (Article) при сохранении данных, и наоборот - преобразование моделей в ViewModels при извлечении данных ... так часто, что я создавал частный метод построения объектов ViewModel:

private ArticleViewModel BuildViewModel(Article a)
{
    return new ArticleViewModel { Title = a.Title /* all properties */ }
}

Продвигаясь вперед, ArticleService предоставляет метод для извлечения всех статей из хранилища данных, возвращая их как ViewModels: public IEnumerable<ArticleViewModel> All()

Класс вызова использует его так: var articleViewModels = _articleService.All();

Простой, верно?

Проблема:

Я изначально писал All() лениво с классическим циклом foreach:

private IEnumerable<ArticleViewModel> All()
{
    var viewModels = new List<ArticleViewModel>();
    foreach (var article in _db.Articles)
        viewModels.Add(BuildViewModel(article));
    return viewModels;
}

Работало нормально - articleViewModels был список всех моделей представлений.

Затем я использовал ReSharper для преобразования этого цикла в оператор LINQ для повышения производительности и красивости, а затем объединил оператор присваивания с оператором возврата. Результат:

private IEnumerable<ArticleViewModel> All()
{
    return _db.Articles.Select(article => BuildViewModel(article)).ToList();
}

Я отладил оператор LINQ, и зверь проснулся:

LINQ to Entities не распознает метод ArticleViewModel BuildViewModel (Article) ', и этот метод не может быть преобразован в выражение хранилища.

Вопрос. Почему этот оператор LINQ нарушает мой код?

Примечание. Возвращение к явному объявлению, присваиванию, возврату сработало с оператором LINQ, поэтому я почти уверен, что это как-то связано с лямбда-логикой.

Ответы [ 3 ]

7 голосов
/ 26 января 2012

Вопрос. Почему этот оператор LINQ нарушает мой код?

Поскольку LINQ to Entities пытается перевести BuildViewModel в SQL.Он не знает как, и поэтому он умирает.

В исходной версии вы передавали объект из базы данных в локальный ящик, а затем выполняли проекцию, используя BuildViewModel на стороне клиента.Это нормально.

, поэтому я почти уверен, что это как-то связано с лямбда-логикой.

Нет.Это потому, что LINQ to Entities не может перевести BuildViewModel в SQL.Не имеет значения, используете ли вы лямбда-выражение для выражения проекции или нет.

Вы можете переписать код следующим образом:

return _db.Articles.
          .AsEnumerable()
          .Select(article => BuildViewModel(article)).ToList();

Это приводит к тому, что _db.Articles обрабатывается как простой старый перечислимый объект, а затем выполняется на стороне клиента проецирования.Теперь LINQ to Entities не должен выяснять, что делать с BuildViewModel.

2 голосов
/ 26 января 2012

Ответ Джейсона объясняет ошибку, но не [не] перечисляет простое исправление (если вы все еще хотите использовать LINQ).Просто добавьте вызов .AsEnumerable () сразу после "_db.Articles".Это заставит любые будущие операторы LINQ выполняться с использованием LINQ to Objects (в памяти), а не пытаться использовать IQueryable для выполнения операторов в базе данных.

0 голосов
/ 26 января 2012

Просто ваша функция BuildViewModel не может быть перенесена в raw SQL. То есть.

...