1. 程式人生 > 其它 >什麼是WPF MVVM應用程式中的依賴注入?如何設定?

什麼是WPF MVVM應用程式中的依賴注入?如何設定?

當解決方案的規模和範圍擴大時,保持整體應用程式的靈活性變得更加困難。 物件之間的依賴關係不斷增長,更改一個類可能需要更新其他類,依賴注入 (DI) 可以幫助解決這一挑戰。

如您所知,依賴注入是“控制反轉”(IoC)程式設計原理的一種形式。 這意味著類不會建立它們所依賴的物件,DI 框架具有負責揭示和解決依賴關係的容器。

DevExpress WPF v21.2正式版下載

依賴注入可以解決哪些問題?

假設您有一個使用資料服務獲取資料的檢視模型:

public class UserViewModel
{
MyDataService dataService;
public UserViewModel() {
this.dataService = new MyDataService();
}
}

檢視模型依賴於一個服務——這意味著 MyDataService 是 UserViewModel 的依賴,直接在檢視模型類中建立服務非常容易,但是這種方法有幾個缺點:

  • 類緊密相連,每次使用 UserViewModel 時,它都會隱式建立 MyDataService 的一個例項。
  • 如果以後修改 MyDataService 的初始化方式,則必須修改 MyDataService 初始化的所有檢視模型。
  • 您不能專門為 UserViewModel 類建立單元測試,因為它依賴於 MyDataService。 如果測試失敗,您可能無法確定錯誤是在 UserViewModel 中還是在 MyDataService 中。

如果將 MyDataService 傳遞給 UserViewModel 的建構函式,則可以避免這些問題:

public class UserViewModel
{
MyDataService dataService;
public UserViewModel(MyDataService dataService) {
this.dataService = dataService;
}
}

不幸的是,這種技術也有缺陷:

  • 在每個類中建立不同的檢視模型,並且它們都必須知道如何建立 MyDataService。
  • 如果不建立靜態屬性,可能很難在多個檢視模型之間共享同一個 MyDataService 例項。

依賴注入的主要思想是集中解決所有依賴,這意味著您的程式中有一個單獨的塊來初始化新的類例項並將引數傳遞給它們。 儘管您可以為此實現自己的邏輯,但使用 DI 框架來幫助避免/消除示例程式碼會更方便。

依賴注入模式具有以下優點:

  • 類之間是鬆散耦合的,因此您可以輕鬆地修改依賴項,例如,將MyDataService替換為MyDataServiceEx。
  • 建立單元測試很容易,因為您可以將模擬引數傳遞給測試的類。
  • 您的專案結構良好,因為始終知道所有依賴項的管理位置。
將依賴注入應用到WPF應用程式

.NET 社群有許多很棒的框架來幫助您在應用程式中實現依賴注入模式,所有這些框架都有兩個主要特點:

  • 您可以在容器中註冊類。
  • 您可以建立具有已初始化依賴項的物件。

容器是 DI 框架中的中心物件,可以自動檢測和解決類依賴關係。 某些框架可以將引數注入類屬性,但最常見的方法是將引數注入建構函式。

讓我們修改 MainViewModel 建構函式,使其接受介面替代類:

public class MainViewModel
{
IDataService dataService;
public MainViewModel(IDataService dataService) {
this.dataService = dataService;
}
}

這將允許您在將來使用不同的 IDataService 實現。

進行此更改後,我們需要建立一個 DI 容器來註冊 MyDataService 並例項化 MainViewModel:

protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
var builder = new ContainerBuilder();
//allow the Autofac container resolve unknown types
builder.RegisterSource(new AnyConcreteTypeNotAlreadyRegisteredSource());
//register the MyDataService class as the IDataService interface in the DI container
builder.RegisterType<MyDataService>().As<IDataService>().SingleInstance();
IContainer container = builder.Build();
//get a MainViewModel instance
MainViewModel mainViewModel = container.Resolve<MainViewModel>();
}

在此示例中,我們使用了 Autofac 框架,但您可以使用任何其他 DI 框架,例如 Unity 或 Ninject。 DI 容器建立 MainViewModel 並自動將 MyDataService 注入 MainViewModel 建構函式,這允許您在每次建立具有 IDataService 引數型別的類時避免 MyDataService 初始化。

然後我們需要將 MainViewModel 連線到它的檢視:MainView,最明顯的策略是在檢視建構函式中設定 DataContext:

public MainView() {
InitializeComponent();
this.DataContext = container.Resolve<MainViewModel>();
}

但是,要訪問 DI 容器,您必須將其設為靜態或將其傳遞給每個檢視建構函式。 一個更好的解決方案是建立一個標記擴充套件,它根據其型別返回一個檢視模型例項:

public class DISource : MarkupExtension
{
public static Func<Type, object> Resolver { get; set; }
public Type Type { get; set; }
public override object ProvideValue(IServiceProvider serviceProvider) => Resolver?.Invoke(Type);
}
<UserControl DataContext="{local:DISource Type=local:MainViewModel}">

最初,標記擴充套件未繫結到任何 DI 容器。 要允許擴充套件使用您的容器,請按以下方式指定檢視模型解析器:

public partial class App : Application
{
protected override void OnStartup(StartupEventArgs e) {
base.OnStartup(e);
//...

IContainer container = builder.Build();
DISource.Resolver = (type) => {
return container.Resolve(type);
};
}
}

這種技術允許 DISource 與任何可能的容器一起工作。

依賴注入和DevExpress服務

在上面的示例中,我們使用了不與可視控制元件通訊的抽象資料服務。 如您所知,許多DevExpress WPF服務使用檢視控制元件,因此服務必須知道使用哪個控制元件。 例如,如果您希望將 NavigationFrameService 注入到檢視模型中,還需要將此服務附加到相應的 NavigationFrame 控制元件。

在檢視模型中建立一個包含服務的公共屬性,並將服務繫結到 NavigationFrame:

public class MainViewModel {
public INavigationService NavigationService { get; }

public MainViewModel(INavigationService navigationService) =>
NavigationService = navigationService;
}
<dxwui:NavigationFrame>
<dxmvvm:Interaction.Behaviors>
<common:AttachServiceBehavior Service="{Binding NavigationService}"/>
</dxmvvm:Interaction.Behaviors>
</dxwui:NavigationFrame>

AttachServiceBehavior 是一個簡單的附加操作,它在服務屬性更改時呼叫 NavigationFrameService.Attach。 雖然 AttachServiceBehavior 不包含在我們的庫中,但您可以在此處獲取其程式碼:How to use our Services with Dependency Injection/AttachServiceBehavior。即使 MainViewModel 使用 NavigationFrameService,它也不必實現 ISupportServices 介面。 此外,導航中涉及的所有子檢視都可以在不附加到 NavigationFrame 的情況下使用該服務——因為它已經在主檢視級別進行了配置。

並非所有DevExpress 服務都需要視覺化元件,某些服務,例如 DXMessageBoxService或 DXOpenFileDialogService,不需要顯式附加,因此您可以將它們作為任何其他非 DevExpress 服務注入。

在某些情況下,如果需要為特定檢視配置服務,則使用 DI 容器注入服務可能並不明智。 例如,如果您有一個繫結到檢視模型命令的服務,就會出現這種情況,直接在檢視中定義服務並在那裡配置所有繫結要容易得多。

DevExpress WPF | 下載試用

DevExpress WPF擁有120+個控制元件和庫,將幫助您交付滿足甚至超出企業需求的高效能業務應用程式。通過DevExpress WPF能建立有著強大互動功能的XAML基礎應用程式,這些應用程式專注於當代客戶的需求和構建未來新一代支援觸控的解決方案。 無論是Office辦公軟體的衍伸產品,還是以資料為中心的商業智慧產品,都能通過DevExpress WPF控制元件來實現。


DevExpress技術交流群6:600715373      歡迎一起進群討論

更多DevExpress線上公開課、中文教程資訊請上中文網獲取