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创建它们,或者将它们从容器中完全移除并自行管理它们。基于直觉,我会倾向于工厂界面。

相关问题