АвтоМаппер (AutoMapper)

АвтоМаппер (AutoMapper) — це об"єкт-об"єкт маппер із одного типу в інший. За допомогою цієї бібліотеки ви зможете легко вирішити багато проблем конвертації подібних об"єктів. АвтоМаппер зробить цей процес автоматичним.

Проблема

Ви коли небуть стикалися із потребою написання коду, який виглядає так:


            Customer customer = GetCustomerFromDB();

            CustomerViewItem customerViewItem = new CustomerViewItem()
                                       {
                                           FirstName = customer.FirstName,
                                           LastName = customer.LastName,
                                           DateOfBirth = customer.DateOfBirth,
                                           NumberOfOrders = customer.NumberOfOrders
                                       };

            ShowCustomerInDataGrid(customerViewItem);


Для прикладу наш сценарій може бути такий:
Наша внутрішня модель класів має сутність Customer, і ми збираємося показувати замовників у дата гріді, для цього нам потрібний набагато легший об"єкт CustomerViewItem , який і буде баундитися до Гріда.

Як ви бачите із вище написаного коду та є 4 лінійки, де ми просто копіюємо значення із одного об"єкта в інший. Але може бути що потрібно буде перемапати 10 або й більше пропертів. Що тоді?

Чи бажали б ви щоб перемапування із Customer  в CustomerViewItem  відбувалося автоматично?

Звичайно що ви б хотіли, особливо коли ви в інший більш делікатній ситуації, коли треба перемапати цілу купу важких об"єктів у більш легкі DTO об"єкти, які будуть слатися за допомогою якихось сервісів через мережу.

AutoMapper

На веб сторінці Авто маппера сказано, що «АвтоМаппер це об»єкт-об"єкт маппер.Такий тип мапінгу використовується для трансформування вхідного об"єкту одного типу в вихідний об"єкт іншого типу. Що робить АвтоМаппер цікавим є те, що він має ряд узгодженостей, за якими він виконує «грязну» роботу визначення як зробити із об"єкта А об"єкт Б. Якщо узгодженості зберігаються, то практично не потрібно додаткової конфігурації щоб перемапати два типи.", а іншими словами, АвтоМаппер дозволяє нам вирішити нашу проблему.

Щоб почати працювати із АвтоМаппером просто скачайте його тут. Це одна-однісінька збірка, тому у вас не винекне проблем із підключенням її у проект.

Щоб попросити АвтоМаппер зробити всю роботу замість вас, все що вам потрібно — це добавити наступну лінійку коду десь на старт апі вашої програми.


            Mapper.CreateMap<Customer, CustomerViewItem>();


Як тільки ви добавили таку лінійку, погляньте який красивий код ми отримуємо для перемапінгу:


            Customer customer = GetCustomerFromDB();

            CustomerViewItem customerViewItem = Mapper.Map<Customer, CustomerViewItem>(customer);

            ShowCustomerInDataGrid(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. Щоб відкрити очі Автомаперу все що потрібно є чуть змінити конфігурацію маппера як нижче:


           Mapper.CreateMap<Customer, CustomerViewItem>()
                .ForMember(cv => cv.FullName, m => m.MapFrom(s => s.FirstName + " " + s.LastName))
				

Ось результати:



Більш складний приклад 2

А що якщо у вас є пропертя Company типу Company:


    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 проблему:


    public class VIPResolver : ValueResolver<bool , string >
    {
        protected override string ResolveCore(bool source)
        {
            return source ? "Y" : "N";
        }
    }

Після цього тільки одна лінійка коду потрібна, щоб усе запрацювало:

.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“.

Коментарі (1)

RSS згорнути / розгорнути
+
0
Так, маппер економить час. Хоча, перший раз коли я його заюзав, потратив 2 години на усунення невідомого багу… Цей баг був повязаий не з самим маппером, а з тим що я його використовував в веб аплікації, і, якимось чином, маппер не хотів працювати правильно зразу ж після внесення змін конфігурації мапування.
avatar

lapsick

  • 14 травня 2010, 00:53

Тільки зареєстровані й авторизовані користувачі можуть залишати коментарі.