1

我真的只是从 MVVM、IoC 和依赖注入开始,我遇到了一个我不知道如何解决的绊脚石,但我明白它为什么会发生。

我将 Castle Windsor 用于 DI 和 IoC 功能,并将 MVVM Light 作为我在 WPF 应用程序中的 MVVM 框架。使用本教程,我设法让 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 负责构建我的视图模型,但它无法处理我的实体。

我做了很多谷歌搜索,但发现这个问题的答案或建议很少,这让我认为我所做的事情是错误的。 这个 Stack Overflow 问题有两个答案表明在视图模型上有一个实体是可以的,但我开始怀疑这是否属实。其他问题,例如这个问题表明实体不应该靠近视图模型。

解决此问题的正确方法是什么?

更新:根据要求,这是异常的堆栈跟踪:

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;
    }
}

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

4

2 回答 2

1

Groupson属性MainPageViewModel创建了一堆不在容器中的虚拟机,但是您的堆栈跟踪是寻找绑定/创建 GroupViewModel 实例的定位器(为什么?我现在无法用您发布的代码来判断。) .

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

于 2012-07-18T19:57:05.330 回答
1

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

除了 Ritch 的回答之外,这个 Stack Overflow 问题和Castle Windsor 的主要贡献者之一 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<Group>(),在上面的代码中没有这一行。没有那个,我从温莎那里得到了一个例外,但不幸的是我没有保留细节,我不能再复制它,所以我的应用程序中可能还有其他问题。

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

于 2012-07-22T21:28:31.883 回答