Данное руководство устарело. Актуальное руководство: Руководство по ASP.NET Core
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 в контроллере:
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 для проецирования одних классов на другие.