Внедрение зависимостей в .NET MAUI. Основные способы получения сервисов

Способы получения сервисов (или разрешение зависимостей) в .NET MAUI могут быть автоматическими и явными. При использовании автоматических способов получения зависимостей .NET MAUI самостоятельно ищет все зарегистрированные сервисы в контейнере DI, настраивает их и возвращает готовый сервис нам. Явные способы получения сервисов подразумевают то, что мы самостоятельно обращаемся к контейнеру DI для получения сервиса.

Как правило, при получении сервиса из контейнера DI выполняется следующая последовательность действий:

  1. если сервис не зарегистрирован, контейнер создает исключение.
  2. если сервис зарегистрирован как singleton, то контейнер при первом вызове создает экземпляр сервиса и возвращает его и сохраняет ссылку.
  3. если сервис зарегистрирован как transient, то контейнер возвращает новый экземпляр и не сохраняет ссылку на него.

Внедрение через конструктор (Constructor Injection)

Наиболее популярным и часто используемым способом получения сервиса является внедрение через конструктор (Constructor Injection). Этот автоматический способ используется в том случае, если зависимость (сервис) является обязательной.

Для того, чтобы воспользоваться этим способом, класс регистрируется как сервис в контейнере DI, а необходимые для класса зависимости перечисляются в его конструкторе. При этом, что касается .NET MAUI, то в приложениях, использующих Shell, нет необходимости регистрировать в контейнере DI страницы приложения. Например, вернемся к нашему приложению с сервисом ILogger.  У нас есть класс страницы MainPage, который использует сервис ILogger. Мы можем получить сервис следующим образом:

public partial class MainPage : ContentPage
{
    int count = 0;

    private readonly ILogger _logger; //сервис

    public MainPage(ILogger logger) 
    {
        InitializeComponent();
        _logger = logger;
    }

    private void OnCounterClicked(object sender, EventArgs e)
    {
        count++;

        if (count == 1)
            CounterBtn.Text = $"Clicked {count} time";
        else
            CounterBtn.Text = $"Clicked {count} times";

        SemanticScreenReader.Announce(CounterBtn.Text);

        _logger.WriteLine(CounterBtn.Text);
    }
}

В конструкторе мы указываем тип сервиса и, при создании страницы, .NET MAUI автоматически вернет нам ILogger, если он будет зарегистрирован одним из методов в контейнере DI.

Явный способ получения сервиса

Явный способ получения сервиса в .NET MAUI используется в том случае, если вызывающий класс содержит только один конструктор без параметров или когда приложение .NET MAUI не использует оболочку (Shell). Также иногда можно встретить другое обозначение этого способа — локатор сервисов (service locator). Способ заключается в том, что мы самостоятельно получаем доступ к реализации IServiceProvider из которого запрашиваем необходимый нам сервис.

Свойство, реализующее IServiceProvider в .NET MAUI содержится в свойстве Element.Handler.MauiContext.Services. Чтобы получить доступ к этому свойству на странице и запросить сервис, рекомендуется использовать следующий подход:

  1. Определить обработчик события HandlerChanged страницы
  2. В обработчике получить доступ к Handler и запросить необходимый сервис.

Такой подход гарантирует нам, что свойство Handler не будет равно null. Например, воспользуемся этим способом в нашем приложении:

public partial class MainPage : ContentPage
{
    int count = 0;

    private ILogger _logger; 

    public MainPage() 
    {
        InitializeComponent();
        HandlerChanged += OnHandlerChanged;
    }


    private void OnHandlerChanged(object sender, EventArgs e)
    {
        if (_logger == null)
        {
            _logger = Handler.MauiContext.Services.GetService<ILogger>();
            //или так
            //Handler.MauiContext.Services.GetRequiredService<ILogger>();
        }
    }

    private async void OnCounterClicked(object sender, EventArgs e)
    {
        count++;

        if (count == 1)
            CounterBtn.Text = $"Clicked {count} time";
        else
            CounterBtn.Text = $"Clicked {count} times";

        SemanticScreenReader.Announce(CounterBtn.Text);
        //используем сервис
        _logger.WriteLine(CounterBtn.Text);

    }
}

Сервис запрашивается из контейнера с использованием метода GetService<T>() или GetRequiredService<T>(). Отличие этих методов состоит в том, что в случае, если сервис не зарегистрирован в контейнере, то GetRequiredService<T>() сгенерирует исключение, а GetService<T>() просто вернет нам null.

Несмотря на то, что этот способ получения сервисов вполне рабочий и может быть использован, общей рекомендацией в .NET при использовании внедрения зависимостей является избегать, если это возможно, использования service locator и использовать другие способы, например, внедрение через конструктор.

Итого

Рассмотренные выше способы получения сервисов в .NET MAUI являются основными, но не единственными. Существуют и другие, менее используемые, способы — внедрение через свойство (Property Injection) или внедрение через метод (Method Injection), которые мы рассмотрим, когда они нам понадобятся.

Подписаться
Уведомить о
guest
0 Комментарий
Старые
Новые Популярные
Межтекстовые Отзывы
Посмотреть все комментарии