國際奧會(控制反轉)
使用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...
}
在這裡,我們有實 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 呢?
服務提供者的名稱中有「服務」,但實際上可以用來解析任何類別的實例,包括 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
,請參閱 這裡。