АвтоМаппер (AutoMapper) — це об"єкт-об"єкт маппер із одного типу в інший. За допомогою цієї бібліотеки ви зможете легко вирішити багато проблем конвертації подібних об"єктів. АвтоМаппер зробить цей процес автоматичним.
Проблема
Ви коли небуть стикалися із потребою написання коду, який виглядає так:
Для прикладу наш сценарій може бути такий:
Наша внутрішня модель класів має сутність Customer, і ми збираємося показувати замовників у дата гріді, для цього нам потрібний набагато легший об"єкт CustomerViewItem , який і буде баундитися до Гріда.
Як ви бачите із вище написаного коду та є 4 лінійки, де ми просто копіюємо значення із одного об"єкта в інший. Але може бути що потрібно буде перемапати 10 або й більше пропертів. Що тоді?
Чи бажали б ви щоб перемапування із Customer в CustomerViewItem відбувалося автоматично?
Звичайно що ви б хотіли, особливо коли ви в інший більш делікатній ситуації, коли треба перемапати цілу купу важких об"єктів у більш легкі DTO об"єкти, які будуть слатися за допомогою якихось сервісів через мережу.
AutoMapper
На веб сторінці Авто маппера сказано, що «АвтоМаппер це об»єкт-об"єкт маппер.Такий тип мапінгу використовується для трансформування вхідного об"єкту одного типу в вихідний об"єкт іншого типу. Що робить АвтоМаппер цікавим є те, що він має ряд узгодженостей, за якими він виконує «грязну» роботу визначення як зробити із об"єкта А об"єкт Б. Якщо узгодженості зберігаються, то практично не потрібно додаткової конфігурації щоб перемапати два типи.", а іншими словами, АвтоМаппер дозволяє нам вирішити нашу проблему.
Щоб почати працювати із АвтоМаппером просто скачайте його . Це одна-однісінька збірка, тому у вас не винекне проблем із підключенням її у проект.
Щоб попросити АвтоМаппер зробити всю роботу замість вас, все що вам потрібно — це добавити наступну лінійку коду десь на старт апі вашої програми.
Mapper.CreateMap<Customer, CustomerViewItem>();
Як тільки ви добавили таку лінійку, погляньте який красивий код ми отримуємо для перемапінгу:
Давайте глянемо на весь код, щоб вам було легше зрозуміти все що я маю і про що буду надалі говорити:
class Program
{
static void Main(string[] args)
{
var program = new Program();
Mapper.CreateMap<Customer, CustomerViewItem>();
program.Run();
}
private void Run()
{
Customer customer = GetCustomerFromDB();
CustomerViewItem customerViewItem = Mapper.Map<Customer, CustomerViewItem>(customer);
ShowCustomerInDataGrid(customerViewItem);
}
private void ShowCustomerInDataGrid(CustomerViewItem customerViewItem){}
private Customer GetCustomerFromDB()
{
return new Customer()
{
DateOfBirth = new DateTime(1987, 11, 2),
FirstName = "Andriy",
LastName = "Buday",
NumberOfOrders = 7
};
}
}
public class Customer
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public int NumberOfOrders { get; set; }
}
public class CustomerViewItem
{
public string FirstName { get; set; }
public string LastName { get; set; }
public DateTime DateOfBirth { get; set; }
public int NumberOfOrders { get; set; }
}
Наступний скріншот запевняє, що я дійсно отримав те що хотів:
Більш складний приклад 1 (кастомний мапінг)
Тепер ми вже знаємо як зробити найпростіше копіювання пропертів. Але що якщо відповідність між назвами не така однозначна? Для прикладу наш клас CustomerViewItem має пропертю FullName, і ми хочемо щоб вона складалася із First- та LastName нашого Customer класу?
Як тільки я додав пропертю public string FullName { get; set; } в клас CustomerViewItem і запустив програму я отримав null значення в моїй проперті. Це не страшно, просто АвтоМаппер ще не знає як отримати FullName з класу Customer. Щоб відкрити очі Автомаперу все що потрібно є чуть змінити конфігурацію маппера як нижче:
public class Customer
{
public Company Company { get; set; }
//...
}
public class Company
{
public string Name { get; set; }
}
і ви хочете записати назву компанії у CompanyName вашого в«ю класу?
public class CustomerViewItem
{
public string CompanyName { get; set; }
//...
}
Як ви думаєте, що потрібно змінити, щоб воно запрацювало?
Відповідь: Нічого! АвтоМаппер просолідує в глубину ваших вкладених класів і якщо імена співпадають він справиться із своєю задачею.
Більш складний приклад 3 (кастомні резолвери)
А що якщо ви маєте булівську проперту VIP:
public class Customer
{
public bool VIP { get; set; }
}
але хочете її зображати як „Y“ або „N“ у CustomerViewItem класі
public class CustomerViewItem
{
public string VIP { get; set; }
}
Ну, ми можемо вирішити цю проблему як із пропертею FullName, але більш правильний вихіб буде мати свій кастомний вирішувач.
Ну давайте створимо його, щоб вирішити нашу VIP проблему:
Після цього тільки одна лінійка коду потрібна, щоб усе запрацювало:
.ForMember(cv => cv.VIP, m => m.ResolveUsing<VIPResolver>().FromMember(x => x.VIP));
Більш складний приклад 4 (кастомні форматери)
А що якщо я хочу, щоб коли я мапаю DateTime проперту у string щоб АвтоМаппер не використовував звичайний ToString(), а якийсь мій власний форматтер?
Я просто застосую метод ToLongDateString хоч це вже може бути що завгодно:
Ось кастомний форматтер:
public class DateFormatter:IValueFormatter
{
public string FormatValue(ResolutionContext context)
{
return ((DateTime) context.SourceValue).ToLongDateString();
}
}
Наступна лінійка потрібна, щоб Автомаппер знав коли використати цей форматтер:
.ForMember(cv => cv.DateOfBirth, m => m.AddFormatter<DateFormatter>());
І ось результати наших старань:
Чудово. Хіба ні? Мій День Народження ще й українською.
Я дійсно надіюся що вам сподобалася моя розповідь, і що у вас є ідеї як використати цю нову лібу, яка зветься „AutoMapper“.
Так, маппер економить час. Хоча, перший раз коли я його заюзав, потратив 2 години на усунення невідомого багу… Цей баг був повязаий не з самим маппером, а з тим що я його використовував в веб аплікації, і, якимось чином, маппер не хотів працювати правильно зразу ж після внесення змін конфігурації мапування.
Коментарі (1)
RSS згорнути / розгорнутиlapsick
Тільки зареєстровані й авторизовані користувачі можуть залишати коментарі.