Przeczytaj w języku angielskim

Udostępnij za pośrednictwem


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 ContactsViewpliku 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.DependencyInjectionprogramu , 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.