Кодировать проекцию SQL и отображение одновременно?

1

Это работает, чтобы вырезать объект 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 для вывода данных.

Теги:
entity-framework-6

3 ответа

2
Лучший ответ

Все, что вам нужно, это определить выражение где-нибудь и использовать его. Например, в 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. Я также добавил перегрузки только с одним родовым типом (сущностью), которые принимают параметр типа. Это было для меня требованием.

  • 0
    «Мне никогда не приходило в голову думать о ПРОСТРАНСТВЕ как о том, что движется!»
  • 1
    Не используйте Mapper.Map для объектов IQueryable, вам нужно использовать пространство имен Automapper.QueryableExtensions . Вы получите очень плохие SQL-запросы, если будете использовать Mapper.Map. См. Прекращение использования AutoMapper в вашем коде доступа к данным для сообщения от автора Automapper, где он объясняет запрашиваемые расширения.
Показать ещё 8 комментариев
2

Вы можете использовать анонимные типы для ограничения того, что выбрать из БД, а затем использовать эти поля для создания своего объекта:

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();
  • 1
    Лучше придерживаться предложения OP о AsEnumerable а не ToArray - это сэкономит вам затраты на создание массива.
  • 0
    @phoog: спасибо!
Показать ещё 6 комментариев
1

Вы против использования сторонних библиотек? 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();
  • 1
    @ Dr.Zim Да, если вы действительно читаете статью, о которой они говорят, вы должны использовать его метод Addresses.Project().To<DTO>() вместо Mapper.Map<DTO>(Addresses) . Этот код был интегрирован в основной проект autopper как пространство имен Automapper.QueryableExtensions вместо отдельной загрузки.
Показать ещё 2 комментария

Ещё вопросы

Сообщество Overcoder
Наверх
Меню