2011-06-24 39 views
3

使用類似Unity,AutoFac或其他的IOC容器,您必須註冊並解析IInterface以獲取實例。你在應用程序類中做所有的根。在頂層使用DependencyInjection,如何通過架構傳遞服務?

做註冊/解決的東西后,我創造我的MainController並通過他們的解決服務,如:

protected void Application_Start(object sender, EventArgs e) 
{ 
    var builder = new ContainerBuilder(); 

    builder.Register<IUserService1, UserService1>(); 
    builder.Register<IUserService2, UserService2>(); 
    builder.Register<IUserService3, UserService3>(); 
      builder.Register<IAnotherService, AnotherService>(); 
    // And many more Services... 

    _container = builder.Build(); 

    var userService1 = _container.Resolve<IUserService1>(); 
    var userService2 = _container.Resolve<IUserService2>(); 
    var userService3 = _container.Resolve<IUserService3>(); 
var anotherService = _container.Resolve<IAnotherService>();  

    var vm = new MainController(userService1,userService2,userService3,anotherService) 
} 

public class MainController 
{  
    private UserController1 _userVM1; 
    private UserController2 _userVM2; 
    private UserController3 _userVM3; 

    public MainController(IUserService1 userService1,IUserService2 userService2,IUserService3 userService3,anotherService) 
    {  
     _userVM1 = new UserController1(userService1,anotherService); 
     _userVM2 = new UserController2(userService2,...,...); 
     _userVM3 = new UserController3(userService3,...,...,...);  
    } 
} 

// Such a Controller class needs to be created 10 times... and what I do here is typical for all Controllers driving the GUI 
public class UserController1 
{ 
    private readonly IUserService1 _userService1; 

    public UserController1(IUserService1 userService1,IAnotherService anotherService) 
    { 
     _userService1 = userService1;   
     //Bind data to GUI 
     UserData1Collection = ConvertModelIntoViewModelCollection(userService1,anotherService); 
    } 

    public ObservableCollection<UserData1> UserData1Collection { get; set; } 

    private ObservableCollection<UserData1ViewModel> ConvertModelIntoViewModelCollection(IAnotherService anotherService) 
    {  
     var userData1ViewModelCollection = new ObservableCollection<UserData1ViewModel>(); 
     _userService1.GetUserData1().ForEach(user => 
     { 
      userData1ViewModelCollection.Add(new UserData1ViewModel(user, anotherService,...)); 
     });   
     return userData1ViewModelCollection; 
    } 
} 

現在的問題是:

有很多通過下降/傳遞槽服務因爲當例如視圖模型的屬性通過gui控件上的lost_focus改變時,我必須調用服務。

這樣就好嗎?你有沒有看到缺點?或者你會怎麼做?

更新

即DI東西是在我的惡性習慣馬西沃攻擊:P

  1. 你的意思是這樣可以嗎?

  2. Btw。我爲什麼要做那個控制器工廠?爲什麼然後不是ServiceFactory ...然後我們又回到ServiceLocator ...

  3. 我該如何得到我的MainViewModel中的控制器實例?通過擴展我的MVM的構造函數與許多額外的參數?結束了30個參數? ...

 

protected override void OnStartup(StartupEventArgs e) 
{ 
    IContainerBuilder builder = new ContainerBuilder(); 

    // Firstly Register ALL existing Services    
    builder.Register<IAdminService, AdminService>(); 
    builder.Register<IDocumentService, DocumentService>(); 
    builder.Register<ILessonPlannerService, LessonPlannerService>(); 
    builder.Register<IMediator, Mediator>(); 
    builder.Register<IMainRepository, MainRepository>();   
    builder.Register<MainViewModel>(); 

    IContainer _container = builder.Build(); 

    // THEN Register ALL Controllers needing the previously registered Services 
    IControllerFactory factory = new ControllerFactory(builder); 
    IDailyPlanner controller1 = factory.Create<IDailyPlanner>(); 
    IWeeklyPlanner controller2 = factory.Create<IWeeklyPlanner>(); 
    SchoolclassAdministrationViewModel controller3 = factory.Create<SchoolclassAdministrationViewModel>(); 

    // THEN Register the mainViewModel(MainController) which should take ALL Services and ALL Controller... WOW thats a massive Ctor param count... is that pure? Did you mean it that way??? 
    MainViewModel mainViewModel = _container.Resolve<MainViewModel>(); 

    //MainWindow mainWindow = _container.Resolve<MainWindow>(); 
    //mainWindow.DataContext = mainViewModel; 
    //mainWindow.ShowDialog(); 
} 

public class ControllerFactory : IControllerFactory 
{ 
    private readonly IContainerBuilder _builder; 
    private readonly IContainer _container; 

    /// <summary> 
    /// Takes the IOC container to register all Controllers 
    /// </summary> 
    public ControllerFactory(IContainerBuilder builder) 
    { 
     _builder = builder; 

     _builder.Register<SchoolclassAdministrationViewModel>(); 
     _builder.Register<IDailyPlanner, LessonPlannerDailyViewModel>(); 
     _builder.Register<IWeeklyPlanner, LessonPlannerWeeklyViewModel>(); 
     _container = _builder.Build(); 
    } 

    /// <summary> 
    /// Returns an Instance of a given Type 
    /// </summary> 
    public T Create<T>() 
    { 
     return _container.Resolve<T>(); 
    } 
} 

UPDATE2

現在我改變了我的代碼,該MainViewModel接受IControllerFactory作爲參數和添加的這兩行代碼的App類:

builder.Register<IControllerFactory, ControllerFactory>(); 
builder.Register<IContainerBuilder, ContainerBuilder>(); 

這樣我不需要傳遞MainViewModel Ctor中的所有控制器,而是從Main FactoryModel獲取Factory中的控制器實例。

有什麼更好的,我可以在這裏做?或者這是一個可接受的好方案?我沒有經驗與DI可言,所以我問:)

UPDATE3

OK我做了一些代碼重構,因此他們明白做別人的意見什麼的最終解決方案:

protected override void OnStartup(StartupEventArgs e) 
{ 
    IContainerBuilder builder = new ContainerBuilder(); 

    // Firstly Register ALL existing Services   
    builder.Register<IAdminService, AdminService>(); 
    builder.Register<IDocumentService, DocumentService>(); 
    builder.Register<ILessonPlannerService, LessonPlannerService>(); 
    builder.Register<IMediator, Mediator>(); 
    builder.Register<IMainRepository, MainRepository>(); 
    builder.Register<IControllerFactory, ControllerFactory>();    
    builder.Register<IDailyPlanner, LessonPlannerDailyViewModel>(); 
    builder.Register<IWeeklyPlanner, LessonPlannerWeeklyViewModel>(); 

    // Just for visual separation THEN register the MainController driving all other Controllers created via the IControllerFactory   
    builder.Register<MainViewModel>(); 

    // Build the container 
    IContainer container = builder.Build(); 

    // THEN Register the MainController which should take ALL IServices and the IFactory 
    MainViewModel mainViewModel = container.Resolve<MainViewModel>(); 

    // LATER in the mainViewModel`s Ctor you can create all 10 Controller instances with the IControllerFactory like this 
    // _dailyPlannerController = controllerFactory.Create<IDailyPlanner>(); 

    MainWindow mainWindow = new MainWindow(); 
    mainWindow.DataContext = mainViewModel; 
    mainWindow.ShowDialog(); 
} 

public class ControllerFactory : IControllerFactory 
{ 
    private readonly IContainer _container; 

    /// <summary> 
    /// Takes the IOC container to resolve all Controllers 
    /// </summary> 
    public ControllerFactory(IContainer container) 
    { 
     _container = container; 
    } 

    /// <summary> 
    /// Returns an Instance of a given Type 
    /// </summary> 
    public T Create<T>() 
    { 
     return _container.Resolve<T>(); 
    } 
} 

@Can,非常感謝你的時間。我學到了很多!

回答

9

在我看來,你誤解了如何使用IoC容器。您不需要創建服務實例並將它們作爲參數傳遞,而需要讓容器爲您解決它們。

例如,您可以重構代碼如下使使用IoC正確:

protected void Application_Start(object sender, EventArgs e) 
{ 
    var builder = new ContainerBuilder(); 

    builder.Register<IUserService1, UserService1>(); 
    builder.Register<IUserService2, UserService2>(); 
    builder.Register<IUserService3, UserService3>(); 
    builder.Register<IAnotherService, AnotherService>(); 

    builder.Register<MainController, MainController>(); 
    // And many more Services... 

    _container = builder.Build(); 

    //let the container inject all the required dependencies into MainController.. 
    var vm = _container.Resolve<MainController>(); 
} 

在這種情況下,容器應控制自己的MainController對象的生命週期,並確保所有的依賴關係(特性和需要初始化的構造函數參數)被注入並填充。

會發生什麼事是容器會明白要創建一個MainController的實例,它需要IUserService1,IUserService2等等,然後通過查看其他類型來查看它是否可以創建這些實例在容器中註冊。這將以遞歸方式完成,以建立一個依賴關係樹,直到一個類的所有依賴關係都可以被滿足。所得到的MainController將已經注入了所有的依賴關係。

理想情況下,您應該在儘可能小的地方調用Resolve(),以便以只有一個根的方式構造應用程序。爲了深入瞭解依賴注入,我強烈推薦Mark Seeman編寫的Dependency Injection in .NET,在我看來,它是DI可以有的最佳介紹之一。

UPDATE:

爲什麼我建議使用的ControllerFactory是因爲你有你的MainController很多UserController的班,並通過所有這些,你最終會得到10+構造函數的參數的依賴,而不是原因提到在創建新控制器時不得不添加更多內容。如果你的視圖模型只依賴於一個控制器,那麼以這種方式使用工廠是沒有意義的,並且你可以直接依賴於所需的控制器。

至於ServiceFactory,它不是必需的,因爲你的每個類都不可能需要所有可用的服務類,只是其中的一些。在這種情況下,最好爲構造函數中的每個服務明確指定它們。

您還應該將所有實例註冊到一個位置(或在小型安裝程序類中),而不是在不同類的構造函數中註冊。

這裏有一個問題,那就是更具體的MVVM應該讓你去如何組織你的類和依賴: How can I combine MVVM and Dependency Injection in a WPF app?

+0

@msfanboy,我建議你創建一個類的ControllerFactory或類似的東西,是負責用於創建您的所有控制器。然後你只需要爲你的控制器提問。 ControllerFactory反過來可以從容器中解析請求的控制器。您獲得的任何控制器都將已經擁有其中的所有服務(如果您正確註冊了它們)。這是ASP.NET MVC世界中與DI容器集成的一個很好使用的模式。 –

+0

好吧現在我可以更進一步,我渴望知道這一切。 ControlloerFactory如何知道容器,它們之間是否應該存在依賴關係?是的,我聽說過馬克西曼斯的書,他已經告訴我了。我會在接下來的6個月裏沒有時間,但在此之後,我非常渴望看到它! – msfanboy

+0

@msfanboy,您只需將它作爲參數傳遞給構造函數即可。您可以使用容器作爲參數手動新建類,然後針對該實例註冊ControllerFactory接口。這幾乎是您的項目中將容器作爲參數傳遞給其中一個類的唯一地方。 –