共用方式為


國際奧會(控制反轉)

使用MVVM模式的應用程式程式代碼基底中可用來增加模組化的常見模式,是使用某種形式的反轉控件。 其中一個最常見的解決方案是使用相依性插入,其包含建立一些插入後端類別的服務(即傳遞為參數至 viewmodel 建構函式)-這可讓使用這些服務的程式代碼不依賴這些服務的實作詳細數據,也可讓您輕鬆地交換這些服務的具體實作。 此模式也可讓您輕鬆地讓平臺特定功能可供後端程式代碼使用,方法是透過服務加以抽象化,然後視需要插入。

MVVM 工具組未提供內建 API 來協助使用此模式,因為此模式已有專用連結庫,例如 Microsoft.Extensions.DependencyInjection 套件,其提供功能完整且功能強大的 DI API 集合,並做為易於設定及使用 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 會在啟動時初始化,並註冊所有應用程式服務和 ViewModel。 另外還有一個新的 Current 屬性,可用來輕鬆地從應用程式中的其他檢視存取 Services 屬性。 例如:

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

// Use the files service here...

這裡的關鍵層面是,每個服務很可能都使用平臺特定的 API,但由於這些 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...
}

在這裡,我們有實 FileLoggerIFileLogger 介面的類型,以及需要 IFilesServiceIConsoleService 實例。 建構函式插入表示 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 呢?

服務提供者的名稱中有「服務」,但實際上可以用來解析任何類別的實例,包括 viewmodels! 上述所述的相同概念仍適用,包括建構函式插入。 假設我們有類型ContactsViewModel,透過其建IPhoneService構函式使用 IContactsService 和實例。 我們可以有如下 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,請參閱 這裡

範例

  • 查看 範例應用程式 (適用於多個 UI 架構),以查看 MVVM 工具組的運作情形。
  • 您也可以在單元測試中找到更多範例。