При работе с данными (и не только) нам часто приходится сталкиваться с необходимость копирования (маппинга) значений свойств одного объекта в новый объект другого типа.
Например предположим, что запрос к базе данных возвращает нам запись в виде объекта, представленного классом Person:
public class Person
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
}
Далее нам необходимо создать новый объект, представленный классом Student:
public class Student
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
public DateTime AdmissionDate { get; set; }
}
и скопировать в его свойства данные из свойств полученного из БД объекта.
Без помощи сторонней библиотеки нам пришлось бы сделать это самим:
// получаем запись из БД
var person = _dbRepository.GetPerson(1);
// копируем значения свойств (осуществляем маппинг)
var student = new Student
{
FirstName = person.FirstName,
LastName = person.LastName,
BirthDate = person.BirthDate
};
А с использованием библиотеки AutoMapper, маппинг производится всего одной строкой кода:
var student = _mapper.Map<Student>(person);
1. Подключение библиотеки AutoMapper к проекту и ее использование
Шаг 1
Добавление в проект NuGet пакетов:
· AutoMapper
· AutoMapper.Extensions.Microsoft.DependencyInjection
Для этого в SolutionExplorer (Обозреватель решений) правой кнопкой мыши жмем по названию рабочего проекта и выбираем Manage NuGet Packages… (Управление пакетами Nuget).
Далее переходим на крайнюю левую вкладку Browse, и в строку поиска вводим название устанавливаемого пакета NuGet.
В левом окне выбираем нужный нам пакет, а в правом жмем кнопку Install.
Дожидаемся окончания установки.
Проделываем эти действия для обоих пакетов.
Шаг 2
Добавляем в проект класс AppMappingProfile:
public class AppMappingProfile : Profile
{
public AppMappingProfile()
{
CreateMap<Person, Student>();
}
}
В generics метода CreateMap первым передаем тип-источник значений, вторым – тип-приемник.
Т.е. в данном примере мы задаем маппинг из объекта Person в объект Student.
Если мы хотим, чтобы маппинг работал в обоих направлениях, добавляем вызов метода-расширения ReverseMap():
CreateMap<Person, Student>().ReverseMap();
Теперь мы можем также маппить объект student в объект person:
var person = _mapper.Map<Person>(student);
Шаг 3
Добавляем AutoMapper в DI контейнер. Для этого в метод ConfigureServices класса Startup.cs добавляем строку:
public void ConfigureServices(IServiceCollection services)
{
// другой код
services.AddAutoMapper(typeof(AppMappingProfile));
// другой код
}
Классов маппинг-профайлов может быть создано несколько. В таком случае, передаем их в параметры метода AddAutoMapper через запятую:
services.AddAutoMapper(typeof(AppMappingProfile), typeof(MappingProfile2));
Шаг 4
Используем маппер.
Теперь маппинг нашего объекта Person в Student происходит в одну строку кода:
var student = _mapper.Map<Student>(person);
Ниже приведен полный код класса, использующего маппинг:
using AutoMapper;
using AutoMapperInAspNetCore.Db;
using AutoMapperInAspNetCore.Models;
namespace AutoMapperInAspNetCore.Mapping
{
public class MappingHelper
{
private readonly IMapper _mapper;
private readonly IDbRepository _dbRepository;
public MappingHelper(IMapper mapper, IDbRepository dbRepository)
{
_mapper = mapper;
_dbRepository = dbRepository;
}
public void DoSomething()
{
// получаем запись из БД
var person = _dbRepository.GetPerson(1);
// Создаем новый объект типа Student и копируем в его свойства
// значения свойств объекта person (осуществляем маппинг)
var student = _mapper.Map<Student>(person);
}
}
}
Здесь в качестве generic в метод Map объекта _mapper передаем тип-приемник (Student), а в пераметре передаем объект-источник (person).
Теперь мы имеем объект student типа Student, со значениями полей из объекта person.
При этом мапятся только те поля, названия которых полностью совпадают у обоих типов.
В данном случае – это поля:
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime BirthDate { get; set; }
2. Маппинг объектов с не совпадающими наименованиями свойств
Для наглядности немного видоизменим класс Student, переименовав его свойства:
public class Student
{
public string Fio { get; set; }
public DateTime Birthday { get; set; }
public DateTime AdmissionDate { get; set; }
}
Теперь наименования свойств объектов person и student не совпадают.
Для того, чтобы маппинг заработал, нам придется дописать в маппинг-профайле явные правила:
public AppMappingProfile()
{
CreateMap<Person, Student>()
.ForMember(dest => dest.Fio, opt => opt.MapFrom(src => $"{src.FirstName} {src.LastName}"))
.ForMember(dest => dest.Birthday, opt => opt.MapFrom(src => src.BirthDate));
}
где параметр dest представляет собой объект-приемник, а src – объект-источник.
В методе MapFrom для поля Fio объекта student мы применили интерполяцию строки –
$"{src.FirstName} {src.LastName}"
результатом которой будет строка вида «Имя Отчество», которая и присвоится свойству Fio объекта student.