Mvvm 如何使用构造函数依赖项注入将集合中的模型提供给它们的ViewModels?

Mvvm 如何使用构造函数依赖项注入将集合中的模型提供给它们的ViewModels?,mvvm,dependency-injection,inversion-of-control,mef,Mvvm,Dependency Injection,Inversion Of Control,Mef,我在我的WPF应用程序中使用构造函数依赖项注入,我一直在运行以下模式,因此我想了解其他人对它的看法,并了解其他解决方案 目标是将ViewModels的层次结构连接到类似的模型层次结构,以便在每个模型中显示信息的责任在于其自己的ViewModel实现。(这种模式在其他情况下也会出现,但MVVM应该是一个很好的例子。) 这里有一个简化的例子。鉴于我有一个模型,该模型包含一系列进一步的模型: public interface IPerson { IEnumerable<IAddress&

我在我的WPF应用程序中使用构造函数依赖项注入,我一直在运行以下模式,因此我想了解其他人对它的看法,并了解其他解决方案

目标是将ViewModels的层次结构连接到类似的模型层次结构,以便在每个模型中显示信息的责任在于其自己的ViewModel实现。(这种模式在其他情况下也会出现,但MVVM应该是一个很好的例子。)

这里有一个简化的例子。鉴于我有一个模型,该模型包含一系列进一步的模型:

public interface IPerson
{
    IEnumerable<IAddress> Addresses { get; }
}

public interface IAddress
{
}
问题是,将子模型提供给相应的子ViewModel的最佳方式是什么

这个例子很简单,但在一个典型的实际案例中,ViewModels有更多的依赖项——每个依赖项都有自己的依赖项(等等)。我正在使用Unity 1.2(尽管我认为这个问题与其他IoC容器相关),我正在使用Caliburn的视图策略来自动查找适当的视图并将其连接到ViewModel

以下是我当前的解决方案:

父ViewModel需要为每个子模型创建一个子ViewModel,因此它在初始化期间使用的构造函数中添加了一个factory方法:

public class PersonViewModel : IPersonViewModel
{
    private readonly Func<IAddress, IAddressViewModel> _addressViewModelFactory;
    private readonly IPerson _person;

    public PersonViewModel(IPerson person,
                           Func<IAddress, IAddressViewModel> addressViewModelFactory)
    {
        _addressViewModelFactory = addressViewModelFactory;
        _person = person;

        Addresses = new ObservableCollection<IAddressViewModel>();
    }

    public ObservableCollection<IAddressViewModel> Addresses { get; private set; }

    public void Initialize()
    {
        foreach (IAddress address in _person.Addresses)
            Addresses.Add(_addressViewModelFactory(address));
    }
}
现在,当初始化
PersonViewModel
时,它会在模型中的每个
地址中循环,并调用
CreateAddressViewModel()
(通过
Func
参数注入)
CreateAddressViewModel()
创建一个临时子容器并注册
IAddress
模型,以便当它从子容器解析
IAddressViewModel
时,
AddressViewModel
通过其构造函数获得正确的注入实例

对我来说,这似乎是一个很好的解决方案,因为ViewModels的依赖关系非常清楚,它们很容易测试,并且不知道IoC容器。另一方面,性能还可以,但不是很好,因为可以创建很多临时子容器。而且我最终得到了很多非常类似的工厂方法

  • 这是使用Unity将子模型注入子ViewModels的最佳方法吗
  • 在其他IoC容器(例如Autofac)中是否有更好(或更快)的方法
  • 如果MEF不是传统的IoC容器,但仍然用于组合对象,那么如何使用MEF解决此问题

根据容器的不同,您能否在工厂的CreateAddressViewModel方法中指定参数(命名或其他)

container.Resolve<IAddressViewModel>(new NamedParameterOverloads() { { "Address", model } };
container.Resolve(新名称参数重载(){{“Address”,model});

根据容器的不同,您的工厂可能必须知道参数的名称(TinyIoC和afaik),或者它可能必须是构造函数依赖项列表中的最后一个(YMMV,取决于容器),这不是很好,但它节省了快速连续创建大量子容器和随后的GC破坏,并且您仍然可以获得所有其他依赖项的DI

当然,如果您的VM也有一个需要相同IAddress的依赖项,那么这种情况就会发生,在这种情况下,子容器可能是一种方法,除非您希望VM了解容器

更新:
如果您使用的容器的子容器使用“last register wins”(我认为Unity会这样做),然后您可以每次将相同的子容器传递到工厂中,让工厂注册新的IAddress-这样您就不会为每次迭代在堆上创建一个新的UnityContainer实例,并且如果您创建了大量项,它应该减少垃圾收集。

的ViewModel示例应用程序显示了如何将模型和ViewModel结合在一起。该示例使用MEF作为依赖项注入框架。

正如您所指出的,如果任何依赖项需要该模型,则指定参数不起作用,这对我来说是一个阻碍。但是,重用子容器是一种可能性。查看我在任何地方都找不到这样的示例-它们倾向于创建一个单一的ViewModel,并随着选择的变化在其上设置模型。也许我找的地方不对,你能告诉我你想要的类吗?
public class PersonViewModel : IPersonViewModel
{
    private readonly Func<IAddress, IAddressViewModel> _addressViewModelFactory;
    private readonly IPerson _person;

    public PersonViewModel(IPerson person,
                           Func<IAddress, IAddressViewModel> addressViewModelFactory)
    {
        _addressViewModelFactory = addressViewModelFactory;
        _person = person;

        Addresses = new ObservableCollection<IAddressViewModel>();
    }

    public ObservableCollection<IAddressViewModel> Addresses { get; private set; }

    public void Initialize()
    {
        foreach (IAddress address in _person.Addresses)
            Addresses.Add(_addressViewModelFactory(address));
    }
}
public class Factory
{
    private readonly IUnityContainer _container;

    public Factory(IUnityContainer container)
    {
        _container = container;
    }

    public void RegisterStuff()
    {
        _container.RegisterInstance<Func<IAddress, IAddressViewModel>>(CreateAddressViewModel);
    }

    private IAddressViewModel CreateAddressViewModel(IAddress model)
    {
        IUnityContainer childContainer = _container.CreateChildContainer();

        childContainer.RegisterInstance(model);

        return childContainer.Resolve<IAddressViewModel>();
    }
}
container.Resolve<IAddressViewModel>(new NamedParameterOverloads() { { "Address", model } };