8

我習慣於Web應用程序中的IoC/DI - 主要是Ninject和MVC3。我的控制器是爲我創建的,填充了所有依賴關係,子依賴性等。在Windows客戶端(WPF)應用程序中執行依賴注入的正確方法

但是,在胖客戶端應用程序中情況有所不同。我必須創建自己的對象,或者我必須恢復到服務定位器風格的方法,在該方法中,我詢問內核(可能通過某個接口,以允許可測試性)爲我提供一個完全依賴的對象。

但是,我看到幾個地方,服務定位器被描述爲反模式。

所以我的問題是 - 如果我想在我的胖客戶端應用程序中受益於Ninject,是否有更好/更正確的方法來獲取所有這些?

  • 可測
  • 正確的DI/IoC的
  • 最小耦合可能量

請注意,我不只是在談論MVVM這裏獲得視圖模型到視圖。這是由需要從內核提供存儲庫類型對象,然後從該存儲庫中獲取注入功能的實體觸發的(數據當然來自數據庫,但它們也需要一些對象作爲參數,具體取決於狀態世界,Ninject知道如何提供)。我能以某種方式做到這一點,而不會將這兩個存儲庫和實體都視爲不可測試的混亂嗎?

如果有什麼不清楚的地方,請告訴我。謝謝!

EDIT 7月14日

我相信提供的兩個答案可能是正確的。然而,我身體的每一根纖維都在與這種變化作鬥爭;其中一些可能是由於缺乏知識造成的,但也有一個具體原因,我爲什麼看不到這種做事方式的優雅;

我在原始問題中沒有很好地解釋這個問題,但問題是我正在編寫一個將被幾個WPF客戶端應用程序使用的庫(最初4-5個,也許更晚)。這些應用程序都在相同的域模型等上運行,因此將其全部保存在一個庫中是保持DRY的唯一方法。然而,這個系統的客戶也有機會編寫他們自己的客戶 - 我希望他們有一個簡單,乾淨的庫來交談。我不想強迫他們在他們的Composition Root中使用DI(在他的書中使用像Mark Seeman這樣的詞) - 因爲與他們剛創建MyCrazySystemAdapter()並使用它相比,HUGELY使事情複雜化。

現在,MyCrazySystemAdapter(名稱選擇,因爲我知道人們會不同意我在這裏)需要由子組件組成,並使用DI組合。 MyCrazySystemAdapter本身不需要注入。它是客戶需要用來與系統通話的唯一接口。因此,一個客戶可以愉快地得到其中的一個,幕後的DI就像魔術一樣發生,並且該對象由使用最佳實踐和原則的許多不同對象組成。

我確實意識到這將是一種想要做事情的有爭議的方式。但是,我也知道將成爲此API客戶端的人員。如果他們看到他們需要學習和連接一個DI系統,並在應用程序入口點(Composition Root)中提前創建它們的整個對象結構,而不是創建一個對象,它們會給我中指和直接搞亂數據庫,並以你難以想象的方式搞砸了。

TL; DR:交付結構合理的API對於客戶來說太麻煩了。我的API需要提供一個單一的對象 - 使用DI和適當的實踐在幕後構建 - 他們可以使用。現實世界有時會超越爲了忠於模式和實踐而將所有東西都倒退的渴望。

回答

5

我建議看看MVVM框架像Caliburn。它們提供與IoC容器的集成。


基本上,您應該在app.xaml中構建完整的應用程序。如果稍後需要創建某些部分,因爲在啓動時尚不知道創建它們的所有內容,則可以將工廠作爲接口(請參見下文)或Func(請參閱Does Ninject support Func (auto generated factory)?)注入需要創建此實例的類。兩者都將在下一個Ninject發行版中得到本地支持。

例如

public interface IFooFactory { IFoo CreateFoo(); } 
public class FooFactory : IFooFactory 
{ 
    private IKernel kernel; 
    FooFactory(IKernel kernel) 
    { 
     this.kernel = kernel; 
    } 

    public IFoo CreateFoo() 
    { 
     this.kernel.Get<IFoo>(); 
    } 
} 

請注意,工廠實現在邏輯上屬於容器配置,而不屬於業務類的實現。

1

我對WPF或MVVM一無所知,但是你的問題基本上是關於如何在不使用服務定位器(或容器直接)的情況下從容器中取出東西,對嗎?
如果是,我可以給你一個例子。

問題是您使用的是一個工廠,而不是內部使用容器。這樣,你實際上只在一個地方使用容器。

注:我會用一個例子與WinForms和不依賴於特定的容器(因爲,正如我所說,我不知道WPF ...... 我使用溫莎城堡,而不是NInject),但由於您的基本問題並不是與WPF/NInject綁定的,因此您應該很容易將我的答案「移植」到WFP/NInject。

工廠看起來是這樣的:

public class Factory : IFactory 
{ 
    private readonly IContainer container; 

    public Factory(IContainer container) 
    { 
     this.container = container; 
    } 

    public T GetStuff<T>() 
    { 
     return (T)container.Resolve<T>(); 
    } 
} 

您的應用程序的主要形式,通過構造函數注入得到這個廠:

public partial class MainForm : Form 
{ 
    private readonly IFactory factory; 

    public MainForm(IFactory factory) 
    { 
     this.factory = factory; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 
    } 
} 

容器被初始化時,應用程序啓動時,主表單已解決(所以它通過構造函數注入獲取工廠)。

static class Program 
{ 
    static void Main() 
    { 
     var container = new Container(); 
     container.Register<MainForm>(); 
     container.Register<IFactory, Factory>(); 
     container.Register<IYourRepository, YourRepository>(); 

     Application.Run(container.Resolve<MainForm>()); 
    } 
} 

現在的主要形式可以使用工廠得到的東西像你的資料庫從容器中:

var repo = this.factory.GetStuff<IYourRepository>(); 
repo.DoStuff(); 

如果您有更多的形式,並希望使用來自那裏的工廠,以及,你只需要將工廠注入到主表單中,然後在啓動時註冊其他表單,並在工廠的主窗體中打開它們。

這是你想知道的嗎?


編輯:
魯本,你當然是對的。我的錯。
我的答案中的全部內容都是我躺在某個地方的舊例子,但是當我發佈我的答案並且沒有仔細閱讀我的舊例子的上下文時,我很着急。

我的舊例子包含了一個主表單,從中可以打開其他任何形式的應用程序。 這就是工廠是什麼,所以你不必注入其他形式通過構造注入到主窗體。
相反,你可以使用工廠開設任何新的形式:

var form = this.factory.GetStuff<IAnotherForm>(); 
form.Show(); 

當然你不需要的工廠只是從一種形式得到倉庫,只要倉庫被傳遞到窗體通過構造函數注入。
如果您的應用程序只包括一些表格,你不需要工廠可言,你可以通過通過構造注射的形式,以及:

public partial class MainForm : Form 
{ 
    private readonly IAnotherForm form; 

    // pass AnotherForm via constructor injection 
    public MainForm(IAnotherForm form) 
    { 
     this.form = form; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 
    } 

    // open AnotherForm 
    private void Button1_Click(object sender, EventArgs e) 
    { 
     this.form.Show(); 
    } 
} 

public partial class AnotherForm : Form 
{ 
    private readonly IRepository repo; 

    // pass the repository via constructor injection 
    public AnotherForm(IRepository repo) 
    { 
     this.repo= repo; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 

     // use the repository 
     this.repo.DoStuff(); 
    } 
} 
+4

-1您的GetStuff()是服裝中的服務定位器。不要這樣做。 (你的答案其餘部分沒有錯,但SLness使這個問題變得簡單) –

+0

Ruben,你是對的。我糾正了我的答案! –

+0

甚至在看到雷莫的答案(我認爲這是正確的方法)之前,我會建議注入工廠(在適當情況下,作爲Funcs http://stackoverflow.com/questions/4840157/does-ninject-support- FUNC-自動生成的工廠/ 4851885#4851885) –

相關問題