MVVM - 在ViewModels中使用实体

时间:2012-07-18 18:38:21

标签: c# mvvm dependency-injection inversion-of-control castle-windsor

我真的刚刚开始使用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);
    }
}

我想我现在更了解这一点。问题实际上与我发布的原始代码无关。问题是实际设置温莎不是吗?我仍然不确定如何解决这个问题。

2 个答案:

答案 0 :(得分:1)

Groups上的属性MainPageViewModel创建了一堆不在容器中的VM,但是你的stacktrace是寻找绑定/创建GroupViewModel实例的Locator(为什么?我不能在这一点上告诉您发布的代码。)。

您在创建GroupViewModel的方式以及容器正在执行的操作方面存在脱节。您需要让Windsor通过工厂界面Factory Interface Documentation创建它们,或者将它们从容器中完全删除并自行管理它们。基于直觉,我倾向于工厂界面。

答案 1 :(得分:1)

Ritch的回答让我朝着正确的方向前进,但我想发布一个单独的答案,以便我能够显示我最终得到的代码,希望它对下一个尝试这样做的人有用。

除了Ritch的回答,this Stack Overflow questionthis 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的魔力,您现在可以从存储库中的实体创建视图模型了!