Wraz z nowym podejściem architektonicznym zaszła potrzba przepisywania obiektów klas domenowych na obiekty typu DTO. W celu uproszczenia sobie sprawy zastosowałem bilbiotekę AutoMapper, która będzie głównym gościem tego posta.

AutoMapper

Jak zwierzęta?

Gdy zajdzie potrzeba przepisywania/mapowania obiektów domeny na DTO można zastosować podejście ręczne. Jako przykład wezmę oklepaną do bólu klasę User oraz UserDto.

public class User
{
    public Guid Id { get; private set; }
    public string Email { get; private set; }
    public string Password { get; private set; }
    public bool IsActive { get; private set; }
    public DateTime CreatedAt { get; private set; }
    public DateTime UpdatedAt { get; private set; }

    //there is more
}
public class UserDto
{
    public Guid Id { get; set; }
    public string Email { get; set; }
}

W swojej aplikacji użyłem metody GetUser, która zwraca obiekt typu UserDto i aby to zrobić muszę recznie dokonać przepisywania właściwości. Obiekt typu User pobierany jest z repozytorium projektu Databoard.Core.

public UserDto GetUser(Guid id)
{
    var user = _userRepository.GetUser(id);
    var userDto = new UserDto();
    userDto.Id = user.Id;
    userDto.Email = user.Email;

    return userDto;
}

Przy każdej zmianie klasy typu DTO będę musiał dokonywać zmiany w przypisywaniu wartości - co jest czasochłonne i może prowadzić do generowania błędów. Cały proces można zautomatyzować przy użyciu biblioteki AutoMapper.

Prawdopodobnie powyższy kod można byłoby mapować korzystając z mechanizmu refleksji, jednak nie jest to łatwe i nie ma sensu wyważać otwartych drzwi.

A można automatycznie!

AutoMapper mała, lekka biblioteka, która pozwala na pozbycie się powyższego kodu służącego do przepisywania wartości, a zostawia czas programiście na zajmowanie się prawdziwymi problemami :-) Napisana przez Jimmy Bogard, źródła dostępne są na GitHub wraz z wyczerpującą wiki.

Aby rozpocząć pracę z bilbioteką należy ją zainstalować korzystając z Nuget - dotnet add package AutoMapper. W moim przypadku dodałem referencję dla dwóch projektów DataBoard.Infrastructure oraz DataBoard.Web dla wersji 6.0.2.

W projekcie DataBoard.Infrastructure utworzyłem statyczną klasę AutoMapperConfig wraz ze statyczną metodą Initializeze zwracanym interfejsem IMapper, który będzie wstrzykiwany przed wywołaniem mapowania. cfg.CreateMap<User, UserDto>(); definiuje z źródło i cel mapowania.

public static class AutoMapperConfig
{
    public static IMapper Initialize()
    {
        var config = new MapperConfiguration(cfg =>
        {
            cfg.CreateMap<User, UserDto>();
        });

        return config.CreateMapper();
    }
}

W Databoard.Web muszę w konfiguracji dodać services.AddSingleton(AutoMapperConfig.Initialize()); ponieważ konfiguracja jest jedna i będzie niezmieniona przez cały cykl życia aplikacji jako procesu.

Teraz można użyć biblioteki zamiast ręcznego przepisywania właściwości. Należy pamiętać o wstrzyknięciu zależności IMapper w konstruktorze klasy, w której zdefiniowana jest metoda, w moim przypadku w klasie UserService.

public UserDto GetUser(Guid id)
{
    var user = _userRepository.GetUser(id);
    return _mapper.Map<User,UserDto>(user);
}

Powyżej przedstawiam najprostsze użycie AutoMappera. Pozwala ona na dużo więcej jak chociażby na mapowanie kolekcji, przypisywanie wartości domyślnych, czy też omijanie właściwości przy mapowaniu, po więcej odsyłam do wiki.