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.

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.