Это работает, чтобы вырезать объект DDL из объекта Address из нашей базы данных:
public class DDL {
public int? id { get; set; }
public string name { get; set; }
}
List<DDL> mylist = Addresses
.Select( q => new DDL { id = q.id, name = q.name })
.ToList();
Тем не менее, мы хотели бы сохранить наши POCO для сопоставлений ViewModel в одном месте за пределами нашего кода контроллера MVC. Мы хотели бы сделать что-то вроде этого:
List<DDL> mylist = Addresses
.Select( q => new DDL(q)) // <-- constructor maps POCO to VM
.ToList();
Но SQL не может использовать конструктор. Инициализатор объекта выше не использует функции для сопоставления полей. Конечно, вы можете сделать .AsEnumerable().Select( q => new DDL(q))
, но это выбирает все поля в SQL (включая данные), отправляет их на С#, затем С# вырезает нужные нам поля ( ужасно неэффективно передавать данные, которые нам не нужны.)
Какие-либо предложения? Мы используем Entity Framework 6 для вывода данных.
Все, что вам нужно, это определить выражение где-нибудь и использовать его. Например, в ViewModel в качестве статического поля только для чтения.
public class SomeViewModel
{
public static readonly Expression<Func<SomeEntity, SomeViewModel>> Map = (o) => new SomeViewModel
{
id = o.id,
name = o.name
}
public int id { get; set; }
public string name { get; set; }
}
// Somewhere in your controller
var mappedAddresses = Addresses.Select(SomeViewModel.Map);
Я лично сделал себе небольшой статичный Mapper, который хранит все карты и использует их для меня. Карты объявляются в статическом инициализаторе во всех моих моделях ViewModels. Результат дает мне нечто, что похоже на AutoMapper, но не требует lib или сложного кода сопоставления (но и не будет делать для вас никакой магии).
Я могу написать что-то вроде этого:
MyCustomMapper.Map<Entity, ViewModel>(entity);
и он перегружен, чтобы принимать IEnumerables, IQueryables и одну ViewModel. Я также добавил перегрузки только с одним родовым типом (сущностью), которые принимают параметр типа. Это было для меня требованием.
Mapper.Map
для объектов IQueryable, вам нужно использовать пространство имен Automapper.QueryableExtensions
. Вы получите очень плохие SQL-запросы, если будете использовать Mapper.Map. См. Прекращение использования AutoMapper в вашем коде доступа к данным для сообщения от автора Automapper, где он объясняет запрашиваемые расширения.
Вы можете использовать анонимные типы для ограничения того, что выбрать из БД, а затем использовать эти поля для создания своего объекта:
List<DDL> mylist = Addresses
.Select( q => new { id, name })
.AsEnumerable()
.Select(i => new DDL(i.id, i.name) // <- On the Enumerable and not on the Queryable
.ToList();
AsEnumerable
а не ToArray
- это сэкономит вам затраты на создание массива.
Вы против использования сторонних библиотек? Automapper QueryableExtensions делает именно то, что вы хотите.
List<DDL> mylist = Addresses
.Project().To<DDL>()
.ToList();
Он даже имеет приятные функции, такие как возможность фильтрации на преобразованном объекте и фильтра, выполняемого на стороне сервера.
List<DDL> mylist = Addresses
.Project().To<DDL>()
.Where(d => d.name = "Smith") //This gets translated to SQL even though it was performed on DDL.
.ToList();
Addresses.Project().To<DTO>()
вместо Mapper.Map<DTO>(Addresses)
. Этот код был интегрирован в основной проект autopper как пространство имен Automapper.QueryableExtensions
вместо отдельной загрузки.