Konzultant a lektor pro [ASP].NET Core & REST API

AutoMapper do robustního kódu nepatří

Miroslav Holec

Miroslav Holec

22. července. 2015 , aktualizace 29. března. 2016

AutoMapper je celkem chytrý nuget package, který umožňuje velmi snadno provádět mapování mezi objekty. Nejtypičtější použití je při mapování datových entit na různé data transfer objekty, například ViewModely. V praxi pak AutoMapper překlápí hodnoty properties z jednoho objektu do druhého a to na základě C# reflexe, případně dalších konfiguračních pravidel, která se definují pomocí Fluent API (extension metod).

Ukázka použití

Pro předvedení myšlenky AutoMapper použiju ukázku mapování produktové entity na ViewModel.

public class Product
{
    public string Title { get; set; }
    public decimal Price { get; set; }
}

public class ProductViewModel
{
    public string Title { get; set; }
    public decimal Price { get; set; }
}

Pro převedení produktu na ViewModel stačí nakonfigurovat mapování

AutoMapper.Mapper.CreateMap<Product, ProductViewModel>();

a od toho momentu je možné všude v kódu používat:

var viewmodel = AutoMapper.Mapper.Map<ProductViewModel>(product);

Vypadá to elegantně. S rostoucím počtem properties na entitě Product se nemusí vývojář o nic starat a nechá vše na automapperu. U jednoduchých projektů, kde jsou si DTO a datové entity podobné téměř 1:1 to funguje příkladně. Jenomže s rostoucí komplexitou projektu začnou vznikat první problémy.

Příliš mnoho automatizace škodí

Najednou má vývojář komplexní model a každá entita řadu cizích klíčů a navázaných objektů nebo kolekcí. Přestává být žádoucí, aby se vše mapovalo. Nebo naopak vzniká potřeba mapovat něco trochu jinak. Elegance jde stranou a z ničehonic vznikají rozsáhlé konfigurace v duchu:

autoMapper.Mapper.CreateMap<Product, ProductViewModel>()
            .ForMember(dest => dest.ImageUrl,
                       opts => opts.MapFrom(src => src.Image.Url))
			.ForMember(dest => dest.CategoryId,
					   opts => opts.Ignore());

Nakonec se to stejně obalí

Dřív nebo později je potřeba některé ViewModely vyrábět chytřeji a zapojit do jejich mapování pokročilejší logiku. Občas na to stačí ještě AutoMapper a dostatek času s hledáním řešení "jak to mám napsat" ale jindy se zkrátka použití AutoMapper obalí a doplní vlastním kódem:

public class ProductMap
{
	public ProductViewModel ToViewModel(Product dbmodel)
    {
        var viewmodel = AutoMapper.Mapper.Map<ProductViewModel>(product);

		if (dbmodel.Images != null && dbmodel.Images.Any())
		{
			viewmodel.ImageUrl = CreateAbsoluteUrl(dbmodel.EshopId, dbmodel.Images.Single());
		}

		return viewmodel;
	}
}

Komplexita roste a s ní nakonec i nejistota:

Lehce na bojišti, těžko na testišti

Kombinace AutoMapperu s vlastní logikou nakonec dožene vývojáře k tomu, aby mapování důkladně otestoval. Za normálních okolností pro (minimálně ta jednoduchá) mapování nepíšu testy ale když se spojí "blackbox" s vlastním kódem, není zbytí. Pak najednou začnou testy odečítat ušetřený čas, protože to, co se nepsalo ručně v aplikačním kódu, to se teď testuje.

Automapper do robustního řešení nepatří

Pokud nedělám skutečně na jednoduchém webovém projektu, je z mého pohledu použití AutoMapperu sázka do loterie a osobně upřednostňuji napsat si vše sám. Pokud přidávám do jedné třídy novou vlastnost, mám možnost se rozhodnout, kde se všude tato vlastnost zpropaguje a zasáhnout do všech mapování. V případě AutoMapperu naopak přemýšlím (namapuje se to? bude to fungovat?). Společně s řadou chytrých pluginů jako ReSharper navíc nezabere napsání pár desítek řádků mnoho času.

Dodatek po 2 měsících

I po dvou měsících intenzivní práce s AutoMappersem se ukazuje, že přinesl více škody než užitku. Problémy se vyskytují například při změněně názvů sloupců na view modelu nebo na entitě. Automapper provede konvezi a pokud nenajde shodu, sloupce se zkrátka nenamapují. Protože se projekt zkompiluje, je často velmi snadné na mapování zapomenout. Podobné problémy pozoruji při mapování nullable hodnot a při konverzi typů s pohyblivou desetinnou čárkou. Automapper se snaží za každou cenu "něco udělat" a často jen vytváří domnění, že všechno funguje správně.


Zeptejte se