我真的刚刚开始使用MVVM,IoC和依赖注入,我遇到了一个我不知道如何解决的绊脚石,但我明白为什么会发生这种情况。
我使用Castle Windsor作为DI和IoC功能,MVVM Light作为我在WPF应用程序中的MVVM框架。使用this tutorial我设法让Castle Windsor创建一个注入构造函数MainPageViewModel
的{{1}}。我在Castle Windsor注册了一个模拟实现。
除了构造函数之外,以下是IGroupRepository
类中唯一的其他代码。
MainPageViewModel
目的是为存储库中的每个组创建一个视图模型。但是,这样做会导致Castle Windsor提供以下异常:
无法创建组件'Planner.ViewModel.GroupViewModel',因为它具有依赖项 要满足。 'Planner.ViewModel.GroupViewModel'正在等待以下依赖项:
- 未注册的服务'Planner.Models.Group'。
我理解这个例外 - Castle Windsor负责构建我的视图模型,但它无法处理我的实体。
我已经做了很多谷歌搜索,但很少找到这个问题的答案或建议,这让我觉得我所做的是错的。 This Stack Overflow question有两个答案,表明在视图模型上有一个实体是可以的,但我开始怀疑这是否属实。其他问题,such as this one表明实体应该远离视图模型。
解决此问题的正确方法是什么?
更新:根据要求,这是例外的堆栈跟踪:
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;
}
}
我认为这是正确的行为,因为以下代码(我相信)拦截对视图模型的构造函数的任何调用并在适当时注入它们。
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
更新2:我认为这解决了Ritch的问题:
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);
}
}
我想我现在更了解这一点。问题实际上与我发布的原始代码无关。问题是实际设置温莎不是吗?我仍然不确定如何解决这个问题。
答案 0 :(得分:1)
Groups
上的属性MainPageViewModel
创建了一堆不在容器中的VM,但是你的stacktrace是寻找绑定/创建GroupViewModel实例的Locator(为什么?我不能在这一点上告诉您发布的代码。)。
您在创建GroupViewModel
的方式以及容器正在执行的操作方面存在脱节。您需要让Windsor通过工厂界面Factory Interface Documentation创建它们,或者将它们从容器中完全删除并自行管理它们。基于直觉,我倾向于工厂界面。
答案 1 :(得分:1)
Ritch的回答让我朝着正确的方向前进,但我想发布一个单独的答案,以便我能够显示我最终得到的代码,希望它对下一个尝试这样做的人有用。
除了Ritch的回答,this Stack Overflow question和this blog post是温莎城堡的主要贡献者之一,KrzysztofKoźmic,所有这些都帮助我以我认为正确的方式解决了这个问题。 / p>
正如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;
}
}
}
最后一步是使用Windsor注册工厂类,这是通过以下代码完成的:
_container.AddFacility<TypedFactoryFacility>();
_container.Register(
Component.For<Group>(),
Component.For<IGroupViewModelFactory>()
.AsFactory());
值得注意的是,我之前链接的问题在上面的代码中没有Component.For<Group>(),
行。没有这个,我得到了温莎的例外,但不幸的是我没有保留细节,我不能再复制它,所以我的申请中可能还有其他不妥。
通过Castle Windsor的魔力,您现在可以从存储库中的实体创建视图模型了!