AutoMapper

Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core

Последнее обновление: 20.02.2020

Automapper позволяет проецировать одну модель на другую, что позволяет сократить объемы кода и упростить программу.

Рассмотрим на примере. Пусть у нас есть следующая модель домена User, объекты которой хранятся в базе данных:

public class User
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Email { get; set; }
}

Для работы с моделью определены следующие классы контекста данных и репозитория:

public class UserContext : DbContext
{
    public DbSet<User> Users { get; set; }
}

public class UserRepository : IRepository<User>
{
    UserContext db;
    public UserRepository()
    {
        db = new UserContext();
    }
    public IEnumerable<User> GetAll()
    {
        return db.Users.ToList();
    }

    public User Get(int id)
    {
        return db.Users.Find(id);
    }

    public void Create(User item)
    {
        db.Users.Add(item);
    }

    public void Update(User item)
    {
        db.Entry(item).State = EntityState.Modified;
    }

    public void Delete(int id)
    {
        User user = db.Users.Find(id);
        if (user != null)
            db.Users.Remove(user);
    }

    public void Save()
    {
        db.SaveChanges();
    }

    private bool disposed = false;

    public virtual void Dispose(bool disposing)
    {
        if (!this.disposed)
        {
            if (disposing)
            {
                db.Dispose();
            }
        }
        this.disposed = true;
    }

    public void Dispose()
    {
        Dispose(true);
        GC.SuppressFinalize(this);
    }
}
interface IRepository<T> : IDisposable
    where T : class
{
    IEnumerable<T> GetAll();
    T Get(int id);
    void Create(T item);
    void Update(T item);
    void Delete(int id);
    void Save();
}

В контроллере предусмотрена базовая CRUD-функциональность, и для каждого представления созданы свои модели view model:

public class IndexUserViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
}

public class CreateUserViewModel
{
    public string FirstName { get; set; }
    public string LastName { get; set; }
    public int Age { get; set; }
    public string Login { get; set; }
}

public class EditUserViewModel
{
    public int Id { get; set; }
    public string Name { get; set; }
    public int Age { get; set; }
    public string Login { get; set; }
}

Таким образом, есть одна модель домена и три модели представления, которые отличаются по набору свойств. Теперь добавим в проект через NuGet пакет Automapper:

Automapper in ASP.NET MVC 5

После добавления применим Automapper в контроллере:

using System.Collections.Generic;
using System.Web.Mvc;
using AutoMapperApp.Models;
using AutoMapper;

namespace AutoMapperApp.Controllers
{
    public class HomeController : Controller
    {
        IRepository<User> repo;
        public HomeController()
        {
            repo = new UserRepository();
        }
        public ActionResult Index()
        {
            // Создание конфигурации сопоставления
            var config = new MapperConfiguration(cfg => cfg.CreateMap<User, IndexUserViewModel>());
			// Настройка AutoMapper
            var mapper = new Mapper(config);
            // сопоставление
            var users = mapper.Map<List<IndexUserViewModel>>(repo.GetAll());
            return View(users);
        }

        public ActionResult Create()
        {
            return View();
        }
        [HttpPost]
        public ActionResult Create(CreateUserViewModel model)
        {
            if (ModelState.IsValid)
            {
                // Настройка конфигурации AutoMapper
                var config = new MapperConfiguration(cfg => cfg.CreateMap<CreateUserViewModel, User>()
                    .ForMember("Name", opt => opt.MapFrom(c => c.FirstName + " " + c.LastName))
                    .ForMember("Email", opt => opt.MapFrom(src => src.Login)));
                var mapper = new Mapper(config);
                // Выполняем сопоставление
                User user = mapper.Map<CreateUserViewModel, User>(model);
                repo.Create(user);
                repo.Save();
                return RedirectToAction("Index");
            }
            return View(model);
        }

        public ActionResult Edit(int? id)
        {
            if (id == null)
                return HttpNotFound();
            // Настройка конфигурации AutoMapper
            var config = new MapperConfiguration(cfg => cfg.CreateMap<User, EditUserViewModel>()
                    .ForMember("Login", opt => opt.MapFrom(src => src.Email)));
            var mapper = new Mapper(config);
            // Выполняем сопоставление
            EditUserViewModel user = mapper.Map<User, EditUserViewModel>(repo.Get(id.Value));
            return View(user);
        }
        [HttpPost]
        public ActionResult Edit(EditUserViewModel model)
        {
            if (ModelState.IsValid)
            {
                // Настройка конфигурации AutoMapper
                var config = new MapperConfiguration(cfg => cfg.CreateMap<EditUserViewModel, User>()
                    .ForMember("Email", opt => opt.MapFrom(src => src.Login)));
                var mapper = new Mapper(config);
                // Выполняем сопоставление
                User user = mapper.Map<EditUserViewModel, User>(model);
                repo.Update(user);
                repo.Save();
                return RedirectToAction("Index");
            }
            return View(model);
        }
    }
}

Вначале создается объект репозитория IRepository. Затем в каждом методе производится конфигурация Automapper и сопоставление объектов.

Чтобы выполнить сопоставление типов, вначале надо определеить конфигурацию маппера, которой передается, какие именно типы и как именно будут сопоставляться. Для создания конфигурации используется конструктор MapperConfiguration:

var config = new MapperConfiguration(cfg => cfg.CreateMap<User, IndexUserViewModel>());

В этот конструктор передается параметр конфигурации, который с помощью различных методов, в частности, метода CreateMap, настраивает сопоставление классов. В данном случае объект User будет сопоставляться с объектом IndexUserViewModel. В данном случае большое значение имеет порядок: так как в методе Index получаем из бд объекты User и из них создаем объекты IndexUserViewModel, то первым в cfg.CreateMap<User,IndexUserViewModel> идет User. При конфигурации сопоставляются одноименные свойства User и IndexUserViewModel.

Однако при создании, например, набор свойств моделей CreateUserViewModel и User отличается, поэтому нам надо использовать дополнительный метод ForMember() для проецирования отдельных свойств одного класса на свойства другого класса:

var config = new MapperConfiguration(cfg => cfg.CreateMap<CreateUserViewModel, User>()
                    .ForMember("Name", opt => opt.MapFrom(c => c.FirstName + " " + c.LastName))
                    .ForMember("Email", opt => opt.MapFrom(src => src.Login)));

То же самое и с редактированием.

После определения конфигурации создается объект маппера, в конструктор которого передается объект конфигурации:

var mapper = new Mapper(config);

При сопоставлении объектов используется метод mapper.Map():

User user = mapper.Map<EditUserViewModel, User>(model);

Причем также можно создавать коллекции объектов:

var users =
    apper.Map<IEnumerable<User>, List<IndexUserViewModel>>(repo.GetAll());

Таким образом, мы можем использовать AutoMapper для проецирования одних классов на другие.

Помощь сайту
Юмани:
410011174743222
Перевод на карту
Номер карты:
4048415020898850