Dependency Injection + dotnet core

Podczas pisania kodu czeka nas bardzo dużo problemów do rozwiązania. Pierwsze kawałki kodu zazwyczaj wyglądały tak, że wszystko było pisane w jednej metodzie lub klasie. Następnie, po przeczytaniu paru książek/artykułów, człowiek zaczął bardziej się zastanawiać nad tym co robi. Podział kodu na klasy: repozytoria, managery, serwisy, i wszystkie inne. Następnie pojawia się problem, jak połączyć te klasy ze sobą.

Dependency Injection

Zakładając że mamy klasę CustomersService która korzysta z repozytorium CustomersRepository, to w takim wypadku repository jest zależnością w serwisie. 

public class CustomersService
{
    private readonly CustomersRepository _customersRepository;

    public CustomersService()
    {
        _customersRepository = new CustomersRepository();
    }
}

Powyższy kod będzie działał ale problem się zacznie pojawiać jeśli będziemy chcieli napisać unit testy albo zmienić data source naszego repository. Z pomocą przychodzi dependency injection, wzorzec który odwraca trochę sytuacje i mówi że mamy przekazywać gotowe komponenty zamiast je tworzyć wewnątrz klasy. Możemy je przekazać na przykład przez konstruktor lub jako właściwości. Przerabiając nasz przykład, zamienimy tworzenie instancji CustomersRepository na przekazanie interfejsu ICustomersRepository.

public class CustomersService
{
    private ICustomersRepository _customersRepository;

    public CustomersService(ICustomersRepository customersRepository)
    {
        _customersRepository = customersRepository;
    }
}

Po zmianach, możemy swobodnie zacząć pisać unit testy oraz CustomersService nie korzysta teraz z konkretnego CustomersRepository tylko z elastycznego rozwiązania przez interfejs. 

Kontenery

Kontenery są to specjalne klasy które odpowiadają za zarządzanie zależnościami. W skrócie mówiąc, można w nich rejestrować typy oraz tworzyć nowe instancje zarejestrowanych typów. Ja zaprezentuję jak to jest rozwiązanie w aplikacji asp.net core. Utworzyłem domyślny projekt dla aspnet core web api, rozszerzyłem ValuesController żeby korzystał z IValuesRepository oraz utworzyłem klasę implementującą ten interfejs.

public class ValuesController : ControllerBase
{
    private readonly IValuesRepository _valuesRepository;

    public ValuesController(IValuesRepository valuesRepository)
    {
        _valuesRepository = valuesRepository;
    }

    // GET api/values
    [HttpGet]
    public IEnumerable<string> Get()
    {
        return _valuesRepository.GetValues();
    }
}

Aby powyższy kod zadziałał, musimy jeszcze w aplikacji/kontenerze zarejestrować IValuesRepository. W aspnet core odbywa się to w klasie Startup.cs w metodzie ConfigureServices. Dodajemy jedną linijkę, czyli mówimy że klasa ValuesRepository ma być podstawiona pod IValuesRepository.

 services.AddTransient<IValuesRepository, ValuesRepository>();

Wiele implementacji

W zależności od potrzeb, możemy zarejestrować wiele implementacji jednego interfejsu. Załóżmy że mamy serwis odpowiedzialny za wysyłanie powiadomień, smsy, email i inne. Przykładowa implementacja może wyglądać tak, tworzymy wspólny intefejs IMessageService z metodą Send(string message). Utwórzmy dwie klasy EmailMessageService oraz TextMessageService, obie implementujące IMessageService. Następnie rejestrujemy je w metodzie ConfigureServices i przekazujemy do kontrolera kolekcję typu IEnumerable<IMessageService>.

public class NotificationsController : ControllerBase
{
    private readonly IEnumerable<IMessageService> _messageServices;

    public NotificationsController(IEnumerable<IMessageService> messageServices)
    {
        _messageServices = messageServices;
    }

    [HttpGet]
    public IEnumerable<string> GetMessageServiceTypes()
    {
        return _messageServices.Select(x => x.GetType().ToString());
    }
}
services.AddTransient<IMessageService, TextMessageService>();
services.AddTransient<IMessageService, EmailMessageService>();

Powyższa metoda GetMessageServiceTypes() zwróci wszystkie zarejestrowane typy.

Cały kod dostępny na github –
https://github.com/brzooz/Blog/tree/master/c%23/DependencyInjectionExample

Scroll to Top