Běžným vzorem, který lze použít ke zvýšení modularity v základu kódu aplikace pomocí vzoru MVVM, je použít určitou formu inverze řízení. Jedním z nejběžnějších řešení je použití injektování závislostí, které spočívá ve vytvoření sady služeb, jež se injektují do backendových tříd (tj. předávají se jako parametry konstruktorům tříd ViewModel) – díky tomu kód, který tyto služby používá, nemusí spoléhat na detaily jejich implementace a zároveň je snadné konkrétní implementace těchto služeb zaměňovat. Tento model také usnadňuje zpřístupnění funkcí specifických pro platformu pro back-endový kód tím, že je abstrahuje prostřednictvím služby, která se pak vloží tam, kde je to potřeba.

Sada MVVM Toolkit neposkytuje integrovaná rozhraní API, která by usnadňovala používání tohoto vzoru, protože již existují specializované knihovny určené přímo k tomuto účelu, například balíček Microsoft.Extensions.DependencyInjection, který poskytuje plnohodnotnou a výkonnou sadu rozhraní API pro DI a slouží jako IServiceProvider, které se snadno nastavuje a používá. Následující průvodce bude odkazovat na tuto knihovnu a poskytne řadu příkladů, jak ji integrovat do aplikací pomocí vzoru MVVM.

Rozhraní API platformy:Ioc

Konfigurace a řešení služeb

Prvním krokem je deklarování IServiceProvider instance a inicializace všech potřebných služeb, obvykle při spuštění. Například v UPW (ale podobné nastavení je možné použít i v jiných architekturách):

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 Zde je vlastnost inicializována při spuštění a všechny aplikační služby a viewmodely jsou registrovány. K dispozici je také nová Current vlastnost, která se dá použít pro snadný přístup k Services vlastnosti z jiných zobrazení v aplikaci. Například:

IFilesService filesService = App.Current.Services.GetService<IFilesService>();

// Use the files service here...

Klíčovým aspektem je, že každá služba může velmi dobře používat rozhraní API specifická pro platformu, ale protože jsou všechny abstrahovány prostřednictvím rozhraní, které náš kód používá, nemusíme se o ně starat, kdykoli pouze řešíme instanci a používáme ji k provádění operací.

Injektáž konstruktoru

Jednou z výkonných funkcí, která je k dispozici, je injektáž konstruktoru, což znamená, že poskytovatel služby DI dokáže automaticky vyřešit nepřímé závislosti mezi registrovanými službami při vytváření instancí požadovaného typu. Zvažte následující službu:

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...
}

Tady máme FileLogger typ, který implementuje IFileLogger rozhraní a vyžaduje IFilesService a IConsoleService instance. Injektování prostřednictvím konstruktoru znamená, že zprostředkovatel služeb DI automaticky zajistí všechny potřebné služby takto:

/// <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>();

Poskytovatel služby DI automaticky zkontroluje, jestli jsou zaregistrované všechny potřebné služby, pak je načte a vyvolá konstruktor pro registrovaný IFileLogger konkrétní typ, aby se instance vrátila.

A co modely zobrazení?

Poskytovatel služeb má v názvu "službu", ale ve skutečnosti se dá použít k překladu instancí jakékoli třídy, včetně modelů viewmodels! Stejné principy vysvětlené výše stále platí, včetně konstruktorové injekce. Představte si, že máme typ ContactsViewModel, který prostřednictvím svého konstruktoru používá instanci IContactsService a instanci IPhoneService. Mohli bychom mít ConfigureServices metodu takto:

/// <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();
}

A pak bychom v našem ContactsViewpřípadě přiřadili kontext dat následujícím způsobem:

public ContactsView()
{
    this.InitializeComponent();
    this.DataContext = App.Current.Services.GetService<ContactsViewModel>();
}

Další dokumenty

Další informace najdete Microsoft.Extensions.DependencyInjectiontady.

Příklady