Способы получения сервисов (или разрешение зависимостей) в .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), которые мы рассмотрим, когда они нам понадобятся.