AutoMapper – konfiguracja

Piotr Wandycz

AutoMapper powstał, aby rozwiązać dość popularny problem z mapowaniem klas. W znacznej większości projektów pojawiają się warstwy i związane z nimi DTOsy (Data Transfer Object). Mapowanie polega na przepisaniu wartości pól z jednej klasy do nowego obiektu o innym typie, np. z PersonDataAccessObject do PersonDataTransferObject. Dzięki temu zostaje zachowana jedna z podstawowych zasad programowania obiektowego – hermetyzacja – bo PersonDataAccessObject może pozostać prywatną klasą, a udostępnione zostanie jedynie nowe “pudełko” na dane. AutoMapper jest potężnym narzędziem, a mimo to jego instalacja jest banalnie prosta.

Szybka instalacja

Przy korzystaniu z .NET Core potrzebujemy dwóch NuGetów: AutoMapper i AutoMapper.Extensions.Microsoft.DependencyInjection. Następnie warto utworzyć plik, gdzie będą znajdować się nasze mapowania:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Food, FoodDto>();
    }
}

Oraz dodać jedną linijkę w konfiguracji serwisów w Startup.cs

public void ConfigureServices(IServiceCollection services)
{
    services.AddRazorPages();
    services.AddAutoMapper(x => x.AddProfile<MappingProfile>(), typeof(Startup));
}

Teraz możemy już wstrzykiwać mapper w miejscu, gdzie go potrzebujemy i wywoływać metodę Map, podając typ docelowy oraz obiekt wejściowy do przemapowania:

public class IndexModel : PageModel
{
    private readonly IMapper _mapper;

    public IndexModel(IMapper mapper)
    {
        _mapper = mapper;
    }

    public void OnGet()
    {
        var food = new Food { Name = "Sernik" };
        var dto = _mapper.Map<FoodDto>(food);
    }
}

Precyzyjne mapowanie

Domyślnie wszystkie wartości gdzie zgadzają się typy i nazwy zostaną przepisane. Możemy jednak w miejscu tworzenia map dopisać własne zachowanie, jeśli coś ma zachowywać się w inny sposób, albo przepisywać się do dodatkowych pól:

public class MappingProfile : Profile
{
    public MappingProfile()
    {
        CreateMap<Food, FoodDto>()
            .ForMember(dest => dest.TotalIngredientsWeight, 
                x => x.MapFrom(src => src.Ingredients.Sum(x => x.Value)));
    }
}

Warto by też utworzyć test jednostkowy sprawdzający, czy nasze mapowania są poprawne. Na szczęście AutoMapper posiada jedną linijkę kodu, która jest za to odpowiedzialna. Pozwala ona wykryć potencjalne przyszłe problemy w aplikacji:

_mapper.ConfigurationProvider.AssertConfigurationIsValid();

Garść wniosków

Jak z większością rzeczy w programowaniu – początkowe zalety mogą stać się w przyszłości wadami. Profile mapowania powinny być robione raczej per biblioteka, żeby nie zrobił się śmietnik. Byłem nawet w projekcie gdzie poszczególne feature foldery posiadały własne profile. Osobiście nie stosuję AutoMappera w swoich rozwiązaniach, ale jeśli już trafiam do projektu, gdzie jest użyty – nie przeszkadza mi to. Mimo wygody w stosowaniu wolę widzieć dokładnie kiedy następuje mapowanie oraz jakie właściwości są przepisywane. Lubię czuć “ból mapowania”.

Istnieje natomiast jeden przypadek, kiedy na pewno zdecydowałbym się na AutoMappera: jeśli byłbym w projekcie, gdzie do odczytu używany jest Entity Framework lub nHibernate. Dzięki projekcjom biblioteka umożliwia ograniczenie odpytywanych kolumn przy wyciąganiu danych z bazy. Jak próbowałem analizować co tam się dokładnie dzieje, to SQL Server Profiler pokazywał mi dziwne rzeczy, więc zainteresowanym polecam po prostu wypowiedź autora. Tutaj można przeczytać o tym więcej: https://docs.automapper.org/en/stable/Queryable-Extensions.html

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *