Ioc (Inversion of control)
Typowym wzorcem, którego można użyć do zwiększenia modułowości w bazie kodu aplikacji przy użyciu wzorca MVVM, jest użycie jakiejś formy inwersji kontrolki. Jednym z najbardziej typowych rozwiązań jest użycie wstrzykiwania zależności, które polega na tworzeniu wielu usług, które są wstrzykiwane do klas zaplecza (tj. przekazywanych jako parametry do konstruktorów modelu widoków) — pozwala to kodowi przy użyciu tych usług nie polegać na szczegółach implementacji tych usług, a także ułatwia zamianę konkretnych implementacji tych usług. Ten wzorzec ułatwia również udostępnianie funkcji specyficznych dla platformy w kodzie zaplecza przez abstrakcję za pośrednictwem usługi, która jest następnie wstrzykiwana w razie potrzeby.
Zestaw narzędzi MVVM Toolkit nie udostępnia wbudowanych interfejsów API ułatwiających korzystanie z tego wzorca, ponieważ istnieją już dedykowane biblioteki specjalnie dla tego typu, takie jak Microsoft.Extensions.DependencyInjection
pakiet, który zapewnia w pełni funkcjonalny i zaawansowany zestaw DI interfejsów API oraz działa jako łatwy do skonfigurowania i użycia IServiceProvider
. Poniższy przewodnik dotyczy tej biblioteki i zawiera szereg przykładów sposobu integrowania jej z aplikacjami przy użyciu wzorca MVVM.
Interfejsy API platformy:
Ioc
Konfigurowanie i rozwiązywanie problemów z usługami
Pierwszym krokiem jest zadeklarowanie IServiceProvider
wystąpienia i zainicjowanie wszystkich niezbędnych usług, zwykle podczas uruchamiania. Na przykład w systemie UWP (ale podobna konfiguracja może być również używana w innych strukturach):
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();
}
}
W tym miejscu właściwość jest inicjowana podczas uruchamiania Services
, a wszystkie usługi aplikacji i modele widoków są zarejestrowane. Istnieje również nowa Current
właściwość, która może służyć do łatwego Services
uzyskiwania dostępu do właściwości z innych widoków w aplikacji. Przykład:
IFilesService filesService = App.Current.Services.GetService<IFilesService>();
// Use the files service here...
Kluczowym aspektem jest to, że każda usługa może bardzo dobrze używać interfejsów API specyficznych dla platformy, ale ponieważ wszystkie te są abstrakcje za pośrednictwem interfejsu używanego przez nasz kod, nie musimy martwić się o nie zawsze, gdy po prostu rozwiązujemy wystąpienie i używamy go do wykonywania operacji.
Wstrzykiwanie konstruktora
Jedną z dostępnych zaawansowanych funkcji jest "wstrzykiwanie konstruktora", co oznacza, że dostawca usług DI może automatycznie rozpoznawać zależności pośrednie między zarejestrowanymi usługami podczas tworzenia wystąpień żądanego typu. Rozważmy następującą usługę:
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...
}
W tym miejscu mamy FileLogger
typ implementowania interfejsu IFileLogger
oraz wymagania IFilesService
i IConsoleService
wystąpień. Wstrzykiwanie konstruktora oznacza, że dostawca usług DI automatycznie zbierze wszystkie niezbędne usługi, w następujący sposób:
/// <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>();
Dostawca usług DI automatycznie sprawdzi, czy wszystkie niezbędne usługi są zarejestrowane, a następnie pobierze je i wywoła konstruktor dla zarejestrowanego IFileLogger
typu konkretnego, aby zwrócić wystąpienie.
Co z modelami widoków?
Dostawca usług ma "usługę" w swojej nazwie, ale może faktycznie służyć do rozpoznawania wystąpień dowolnej klasy, w tym modelu widoków! Te same pojęcia, które wyjaśniono powyżej, nadal mają zastosowanie, w tym iniekcja konstruktora. Załóżmy, że mieliśmy ContactsViewModel
typ, używając IContactsService
wystąpienia i IPhoneService
za pośrednictwem jego konstruktora. Możemy mieć metodę podobną do następującej 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();
}
Następnie w naszym ContactsView
pliku przypiszemy kontekst danych w następujący sposób:
public ContactsView()
{
this.InitializeComponent();
this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}
Więcej dokumentów
Aby uzyskać więcej informacji na temat Microsoft.Extensions.DependencyInjection
programu , zobacz tutaj.
Przykłady
- Zapoznaj się z przykładową aplikacją (dla wielu struktur interfejsu użytkownika), aby zobaczyć, jak działa zestaw narzędzi MVVM Toolkit.
- Więcej przykładów można również znaleźć w testach jednostkowych.