【问题标题】:MVVM - Using entities in ViewModelsMVVM - 在 ViewModel 中使用实体
【发布时间】:2012-07-18 18:38:20
【问题描述】:

我真的刚刚开始使用 MVVM、IoC 和依赖注入,我遇到了一个我不知道如何解决的绊脚石,但我明白为什么会这样。

我在 WPF 应用程序中将 Castle Windsor 用于 DI 和 IoC 功能,并将 MVVM Light 作为我的 MVVM 框架。使用this tutorial,我设法让Castle Windsor 创建一个MainPageViewModel,将IGroupRepository 注入到构造函数中。我已经在温莎城堡注册了一个模拟实现。

以下是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;
    }
}

目的是为存储库中的每个组创建一个视图模型。但是,这样做会导致 Castle Windsor 给出以下异常:

无法创建组件“Planner.ViewModel.GroupViewModel”,因为它具有依赖项 要满足。 'Planner.ViewModel.GroupViewModel' 正在等待以下依赖项:

  • 未注册的服务“Planner.Models.Group”。

我了解此异常 - Castle Windsor 负责构建我的视图模型,但它无法处理我的实体。

我在谷歌上搜索了很多,但对这个问题的答案或建议却很少,这让我认为我所做的事情是错误的。 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;
    }
}

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

【问题讨论】:

  • 通读您的示例,我看不出您在哪里要求 Castle Windsor 创建 GroupViewModel。在示例代码中,您直接调用构造函数,这不会涉及 Castle Windsor。你能显示异常的堆栈跟踪吗?
  • 容器如何绑定到您正在创建的 GroupViewModels?
  • @RitchMelton 我已经用我认为可以回答您问题的代码更新了问题。我还用我的一些新见解对其进行了更新。

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


【解决方案1】:

MainPageViewModel 上的属性 Groups 创建了一堆不在容器中的 VM,但您的堆栈跟踪是寻找绑定/创建 GroupViewModel 实例的定位器(为什么?我现在不知道使用您发布的代码。)。

您对GroupViewModels 的创建方式和容器的作用存在脱节。您要么需要让 Windsor 通过工厂接口 Factory Interface Documentation 创建这些,要么将它们完全从容器中移除并自行管理。凭直觉,我会倾向于工厂界面。

【讨论】:

    【解决方案2】:

    Ritch 的回答将我引向了正确的方向,但我想发布一个单独的答案,以便我可以展示我最终得到的代码,希望它对下一个尝试这样做的人有用。

    除了 Ritch 的回答,this Stack Overflow questionthis blog post 由温莎城堡的主要贡献者之一 Krzysztof Koź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;
            }
        }
    }
    

    最后一步是向 Windsor 注册工厂类,这是通过这段代码完成的:

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

    值得注意的是,我之前链接的问题在上面的代码中没有Component.For&lt;Group&gt;(), 行。如果没有这个,我从 Windsor 那里得到了一个例外,但不幸的是我没有保留细节,我无法再复制它,所以我的应用程序中可能还有其他问题。

    通过 Castle Windsor 的魔力,您现在可以从存储库中的实体创建视图模型!

    【讨论】:

      猜你喜欢
      • 2011-05-08
      • 2013-01-16
      • 2010-11-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多