1

我真的剛剛開始使用MVVM,IoC和依賴注入,我碰到了一個絆腳石,我不知道如何解決,但我明白爲什麼會發生。MVVM - 在ViewModels中使用實體

我使用Castle Windsor的DI和IoC功能以及MVVM Light作爲WPF應用程序中的MVVM框架。使用this tutorial我已經設法讓Castle Windsor創建一個MainPageViewModel,它有一個IGroupRepository注入到構造函數中。我在Castle Windsor註冊了這個模擬實現。

以下是除構造函數外MainPageViewModel類中唯一的其他代碼。

public ObservableCollection<GroupViewModel> Groups 
{ 
    get 
    { 
     var groupVms = new ObservableCollection<GroupViewModel>(); 
     IEnumerable<Group> groups = _repository.GetAllGroups(); 
     foreach (Group g in groups) 
     { 
      var vm = new GroupViewModel(g); 
      groupVms.Add(vm); 
     } 

     return groupVms; 
    } 
} 

其目的是爲存儲庫中的每個組創建一個視圖模型。然而,這樣做會導致溫莎城堡給以下異常:

無法創建組件「Planner.ViewModel.GroupViewModel」,因爲它有依賴 得到滿足。 「Planner.ViewModel.GroupViewModel」正在等待以下依賴性:

  • 服務「Planner.Models.Group」這是未註冊。

我明白這一點例外 - 溫莎城堡是負責構建我的視圖模型,但它沒有處理我的實體的方式。

我已經做了大量的谷歌搜索,但發現很少的答案或建議這個問題,這讓我覺得我所做的是錯誤的。 This Stack Overflow question有兩個答案,這表明在視圖模型上有一個實體是好的,但我開始懷疑這是否屬實。其他問題,such as this one表明該實體應該遠離視圖模型。

解決此問題的正確方法是什麼?

更新:按照要求,這是異常的堆棧跟蹤:

at Castle.MicroKernel.Handlers.DefaultHandler.AssertNotWaitingForDependency() 
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden) 
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired) 
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context) 
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, IDictionary additionalArguments, IReleasePolicy policy) 
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, IDictionary arguments, IReleasePolicy policy) 
at Castle.MicroKernel.DefaultKernel.Resolve(Type service, IDictionary arguments) 
at Castle.Windsor.WindsorContainer.Resolve(Type service) 
at Planner.ViewModel.ViewModelResolver.Resolve(String viewModelName) in D:\Planner\Planner\Planner\ViewModel\ViewModelResolver.cs:line 27 
at Planner.ViewModel.ViewModelLocator.get_Item(String viewModelName) in D:\Planner\Planner\Planner\ViewModel\ViewModelLocator.cs:line 21 

我認爲這是因爲下面的代碼正確的行爲,這(我相信)攔截任何調用構造函數視圖模型,並適當注入它們。

public class ViewModelResolver : IViewModelResolver 
{ 
    private IWindsorContainer _container; 

    public object Resolve(string viewModelName) 
    { 
     if (_container == null) 
     { 
      _container = new WindsorContainer(); 
      _container.Install(new WindsorViewsInstaller()); 
      _container.Install(new WindsorRepositoriesInstaller()); 
     } 

     var viewModelType = 
      GetType() 
      .Assembly 
      .GetTypes() 
      .Where(t => t.Name.Equals(viewModelName)) 
      .FirstOrDefault(); 

     return _container.Resolve(viewModelType); 
    } 
} 

更新2:我覺得這回答Ritch的查詢:

public class ViewModelLocator : DynamicObject 
{ 
    public IViewModelResolver Resolver { get; set; } 

    public ViewModelLocator() 
    { 
     Resolver = new ViewModelResolver(); 
    } 

    public object this[string viewModelName] 
    { 
     get 
     { 
      return Resolver.Resolve(viewModelName); 
     } 
    } 

    public override bool TryGetMember(GetMemberBinder binder, out object result) 
    { 
     result = this[binder.Name]; 
     return true; 
    } 
} 

我想我現在明白這一點了。問題實際上並不是我發佈的原始代碼。問題是發生實際設置溫莎不是嗎?我仍然不確定我是如何解決這個問題的。

+0

通讀您的示例,我沒有看到您要求Castle Windsor創建GroupViewModel的位置。在示例代碼中,您直接調用構造函數,而不涉及Castle Windsor。你能顯示異常的堆棧跟蹤嗎? – phoog 2012-07-18 19:09:23

+0

容器如何綁定到您創建的GroupViewModel? – 2012-07-18 19:44:50

+0

@RitchMelton我已經用我認爲可以回答你的問題的代碼更新了這個問題。我還用一些新的見解對它進行了更新。 – Stu 2012-07-18 19:51:49

回答

1

Ritch的回答讓我朝着正確的方向發展,但我想發佈一個單獨的答案,以便我能夠展示我最終結束的代碼,希望對下一個嘗試這樣做的人有用。

除了裏奇的回答,this Stack Overflow questionthis blog post由Castle Windsor,KrzysztofKoźmic的主要貢獻者之一,都幫助我解決了這個問題,我相信這是正確的方法。如Ritch所說,我不應該直接爲我的視圖模型調用構造函數 - 這就是容器的用途。所以要做到這一點的方法是創建一個能夠幫助Windsor創建視圖模型的類。這被稱爲打字工廠設施。這些類的好處是你並不需要實現它們 - 它們只是接口。這是最終將被用於創建視圖模型類代碼:

public interface IGroupViewModelFactory 
{ 
    GroupViewModel Create(Group group); 
} 

這被注入的構造將創建GroupViewModel這在我的情況是MainWindowViewModel類視圖模型。以下是一個類的代碼:

public class MainWindowViewModel : ViewModelBase 
{ 
    private readonly IGroupRepository _repository; 
    private readonly IGroupViewModelFactory _groupViewModelFactory; 

    public MainWindowViewModel(IGroupRepository repository, IGroupViewModelFactory groupViewModelFactory) 
    { 
     _repository = repository; 
     _groupViewModelFactory = groupViewModelFactory; 
    } 

    public ObservableCollection<GroupViewModel> Groups 
    { 
     get 
     { 
      var groupVms = new ObservableCollection<GroupViewModel>(); 
      IEnumerable<Group> groups = _repository.GetAllGroups(); 
      foreach (Group g in groups) 
      { 
       var vm = _groupViewModelFactory.Create(g); 
       groupVms.Add(vm); 
      } 

      return groupVms; 
     } 
    } 
} 

的最後一步是註冊與溫莎工廠類和通過這段代碼完成:

_container.AddFacility<TypedFactoryFacility>(); 

_container.Register(
    Component.For<Group>(), 
    Component.For<IGroupViewModelFactory>() 
    .AsFactory()); 

值得一提的是,這個問題我鏈接到前面的代碼中沒有Component.For<Group>(),行。沒有這一點,我得到了溫莎的例外,但不幸的是我沒有保留細節,我不能再複製它,所以在我的應用程序中可能還有其他不妥之處。

通過Castle Windsor的魔力,您現在可以從存儲庫中的實體創建視圖模型!

1

MainPageViewModel上的屬性Groups創建了一堆不在容器中的虛擬機,但是您的堆棧跟蹤是尋找綁定/創建GroupViewModel實例的Locator(爲什麼?我現在無法告訴代碼你已經發布。)。

您在創建GroupViewModel的方式以及容器的操作方面已斷開連接。您可能需要讓Windsor通過工廠界面Factory Interface Documentation創建它們,或者將它們從容器中完全移除並自行管理它們。基於直覺,我會傾向於工廠界面。

相關問題