Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
Распространенный шаблон, который можно использовать для увеличения модульности в базе кода приложения с помощью шаблона MVM, заключается в использовании некоторой формы инверсии элемента управления. Одним из наиболее распространенных решений, в частности, является использование внедрения зависимостей, которое состоит в создании ряда служб, внедренных в внутренние классы (т. е. передаваемых в качестве параметров конструкторам viewmodel) — это позволяет коду использовать эти службы не полагаться на сведения о реализации этих служб, а также упрощает переключение конкретных реализаций этих служб. Этот шаблон также позволяет легко сделать функции, характерные для платформы, доступными для внутреннего кода, абстрагируя их через службу, которая затем внедряется по мере необходимости.
MVVM Toolkit не предоставляет встроенных API для упрощения использования этого шаблона, так как для этого уже существуют специализированные библиотеки, например пакет Microsoft.Extensions.DependencyInjection, который предоставляет полнофункциональный и мощный набор API для DI, а также выступает в качестве IServiceProvider, который легко настраивать и использовать. В следующем руководстве приведены примеры интеграции библиотеки в приложения с помощью шаблона MVVM.
API платформы:
Ioc
Настройка и устранение неполадок служб
Первым шагом является объявление экземпляра IServiceProvider и инициализация всех необходимых служб, как правило, при запуске. Например, в UWP (но аналогичную настройку также можно использовать в других платформах):
public sealed partial class App : Application
{
public App()
{
Services = ConfigureServices();
this.InitializeComponent();
}
/// <summary>
/// Gets the current <see cref="App"/> instance in use
/// </summary>
public new static App Current => (App)Application.Current;
/// <summary>
/// Gets the <see cref="IServiceProvider"/> instance to resolve application services.
/// </summary>
public IServiceProvider Services { get; }
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFilesService, FilesService>();
services.AddSingleton<ISettingsService, SettingsService>();
services.AddSingleton<IClipboardService, ClipboardService>();
services.AddSingleton<IShareService, ShareService>();
services.AddSingleton<IEmailService, EmailService>();
return services.BuildServiceProvider();
}
}
Здесь свойство Services инициализируется при запуске, а также регистрируются все сервисы приложения и модели представления. Также появилось новое свойство Current, которое можно использовать для удобного доступа к свойству Services из других представлений приложения. Например:
IFilesService filesService = App.Current.Services.GetService<IFilesService>();
// Use the files service here...
Ключевой аспект здесь в том, что каждый сервис вполне может использовать API, специфичные для платформы, но поскольку все они скрыты за интерфейсом, который использует наш код, нам не нужно о них беспокоиться, когда мы просто получаем экземпляр и используем его для выполнения операций.
Внедрение через конструктор
Одна из мощных функций, которая доступна, — "внедрение конструктора", что означает, что поставщик служб DI может автоматически разрешать косвенные зависимости между зарегистрированными службами при создании экземпляров запрашиваемого типа. Рассмотрим следующую службу:
public class FileLogger : IFileLogger
{
private readonly IFilesService FileService;
private readonly IConsoleService ConsoleService;
public FileLogger(
IFilesService fileService,
IConsoleService consoleService)
{
FileService = fileService;
ConsoleService = consoleService;
}
// Methods for the IFileLogger interface here...
}
Здесь у нас есть тип FileLogger, реализующий интерфейс IFileLogger, которому требуются экземпляры IFilesService и IConsoleService. Внедрение через конструктор означает, что поставщик служб DI автоматически получит все необходимые службы, например:
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
services.AddSingleton<IFilesService, FilesService>();
services.AddSingleton<IConsoleService, ConsoleService>();
services.AddSingleton<IFileLogger, FileLogger>();
return services.BuildServiceProvider();
}
// Retrieve a logger service with constructor injection
IFileLogger fileLogger = App.Current.Services.GetService<IFileLogger>();
Поставщик служб DI автоматически проверит, зарегистрированы ли все необходимые службы, затем получит их и вызовет конструктор зарегистрированного конкретного типа IFileLogger, чтобы получить экземпляр, который нужно вернуть.
А как насчёт моделей представления?
Поставщик услуг имеет "службу" в своем имени, но на самом деле его можно использовать для разрешения экземпляров любого класса, включая viewmodels! Те же концепции, о которых говорилось выше, по-прежнему применимы, включая внедрение зависимостей через конструктор. Представьте, что у нас есть тип ContactsViewModel, использующий экземпляры IContactsService и IPhoneService, передаваемые через его конструктор. У нас мог бы быть ConfigureServices такой метод:
/// <summary>
/// Configures the services for the application.
/// </summary>
private static IServiceProvider ConfigureServices()
{
var services = new ServiceCollection();
// Services
services.AddSingleton<IContactsService, ContactsService>();
services.AddSingleton<IPhoneService, PhoneService>();
// Viewmodels
services.AddTransient<ContactsViewModel>();
return services.BuildServiceProvider();
}
А затем в нашем ContactsViewприложении мы назначим контекст данных следующим образом:
public ContactsView()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}
Дополнительные документы
Дополнительные сведения о Microsoft.Extensions.DependencyInjection см. здесь.
Примеры
- Ознакомьтесь с примером приложения (для нескольких платформ пользовательского интерфейса), чтобы просмотреть набор средств MVVM в действии.
- Дополнительные примеры можно найти в модульных тестах.
MVVM Toolkit