Способы получения сервисов (или разрешение зависимостей) в .NET MAUI могут быть автоматическими и явными. При использовании автоматических способов получения зависимостей .NET MAUI самостоятельно ищет все зарегистрированные сервисы в контейнере DI, настраивает их и возвращает готовый сервис нам. Явные способы получения сервисов подразумевают то, что мы самостоятельно обращаемся к контейнеру DI для получения сервиса.
Как правило, при получении сервиса из контейнера DI выполняется следующая последовательность действий:
- если сервис не зарегистрирован, контейнер создает исключение.
- если сервис зарегистрирован как singleton, то контейнер при первом вызове создает экземпляр сервиса и возвращает его и сохраняет ссылку.
- если сервис зарегистрирован как 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.Maui. Чтобы получить доступ к этому свойству на странице и запросить сервис, рекомендуется использовать следующий подход:
- Определить обработчик события
HandlerChangedстраницы - В обработчике получить доступ к
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), которые мы рассмотрим, когда они нам понадобятся.