Синтаксис лямбда-проекции - PullRequest
6 голосов
/ 28 октября 2010

У меня есть IQueryable, чьи объекты Entity Framework 4 я бы хотел проецировать на их эквиваленты DTO. Одним из таких объектов «Person» является класс EF4, а соответствующий POCO PersonP является классом, который я определил. Я использую Automapper для сопоставления между ними. Тем не менее, когда я пытаюсь следующий код:

IQueryable<Person> originalModel = _repo.QueryAll();
IQueryable<PersonP> projection = originalModel.Select(e => Mapper.Map<Person, PersonP>(e));

Проекция генерирует эту ошибку во время выполнения:

LINQ to Entities does not recognize the method 'TestSite.Models.PersonP Map[Person,PersonP](TestSite.DataLayer.Model.Person)' method, and this method cannot be translated into a store expression.

Каков подходящий синтаксис для создания проекции IQueryable<PersonP> с использованием Automapper? Спасибо.

P.S. Automapper настроен правильно - я использую его в других местах для преобразования туда и обратно между Person и PersonP, т.е. Mapper.Map<Person, PersonP>(myPersonObject) правильно возвращает объект PersonP.

РЕДАКТИРОВАТЬ (больше кода):

Я использую это для вспомогательной функции для привязки POCO сущностей EF4 (PersonP) к Telerik Grid, которая не будет сериализовать сами сущности должным образом, так как они содержат циклические ссылки (то есть свойства навигации). Мой код выглядит так:

public static GridModel GetGridModel<TEntity, TPoco>(IRepository<TEntity> repo, GridState gridState) where TEntity : EntityObject
{
 var originalModel = repo.QueryAll().ToGridModel(gridState);
 var projection = originalModel.Select(e => Mapper.Map<TEntity, TPoco>(e));


 return projection.ToGridModel(gridState); // applies filters, sorts, pages, etc...
}

метод .ToGridModel является методом расширения в IQueryable, и он возвращает сложный объект, который я не могу надежно проанализировать - поэтому это наводит меня на мысль, что я должен выполнить фильтрацию после того, как я выполнил проекцию на POCO.

ОБНОВЛЕНИЕ 2:

Пытаясь упростить вещи, я создал неуниверсальный метод, подобный этому:

public static GridModel GetGridModel2(IRepository<Client> repo, GridState gridState)
{
 IQueryable<Client> originalModel = repo.QueryAll();
 IQueryable<ClientP> projection = originalModel.Select(c => ClientToClientP(c));

 return projection.ToGridModel(gridState);
}

private static ClientP ClientToClientP(Client c)
{
 return new ClientP { Id = c.Id, FirstName = c.FirstName };
}

Этот код также не работает при создании проекции. Я заметил, что IQueryable.Select () имеет несколько перегрузок: выражение> является одним из них. Могу ли я представить этот вызов функции / делегата, используя одну из этих перегрузок?

Ответы [ 3 ]

11 голосов
/ 16 июля 2011

Это фактически было решено автором AutoMapper здесь: http://lostechies.com/jimmybogard/2011/02/09/autoprojecting-linq-queries/

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

Также см. Следующую статью *1007* Пола Хайлса о некоторых улучшениях кэширования.

Надеюсь, это поможет.

5 голосов
/ 28 октября 2010

Какой синтаксис подходит для создания проекции IQueryable с использованием Automapper?

Нет ни одного. Automapper не делает этого .Это неподходящий инструмент для этой работы.

Можно создать инструмент, подобный Automapper, чтобы делать аналогичные вещи для проекций запросов.Я рассматривал это в прошлом, но всегда делал вывод, что код, использующий его, будет менее читабельным, чем проекция.Я не хочу оптимизировать время написания кода над временем чтения кода.

Ваш обновленный код не работает, потому что он не является выражением.Если вы сделаете:

private static Expression<Func<Client, ClientP>> ClientP ClientToClientP()
{
    return c => new ClientP { Id = c.Id, FirstName = c.FirstName };
}

... и затем:

IQueryable<Client> originalModel = repo.QueryAll();
Expression<Func<Client, ClientP>> exp = ClientToClientP();
IQueryable<ClientP> projection = originalModel.Select(exp);

... тогда это будет работать.

2 голосов
/ 28 октября 2010

Если вы добавите .ToList () перед Select, вы можете заставить отображение происходить на стороне клиента (Linq to Objects) вместо стороны сервера (SQL).Просто убедитесь, что вы сначала выполнили фильтрацию, чтобы не переносить всю таблицу.

...