2010-05-10 15 views
10

我在我的WPF应用程序中使用构造函数依赖注入,并且我一直运行到以下模式,因此想要获得其他人的意见并听取其他解决方案。如何使用构造函数依赖注入将模型从集合提供给ViewModels?

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

这是一个简化的例子。既然我有了进一步的模型集合的模式:

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

public interface IAddress 
{ 
} 

我想反映这个层次的的ViewModels,这样我可以绑定一个列表框(或其他)在人视图模型的集合:

public interface IPersonViewModel 
{ 
    ObservableCollection<IAddressViewModel> Addresses { get; } 
    void Initialize(); 
} 

public interface IAddressViewModel 
{ 
} 

子视图模型需要出示从子模型的信息,所以它是通过构造函数注入:

public class AddressViewModel : IAddressViewModel 
{ 
    private readonly IAddress _address; 

    public AddressViewModel(IAddress address) 
    { 
     _address = address; 
    } 
} 

的问题是,什么是最好的方法将子模型提供给相应的子ViewModel?

该示例很简单,但在典型的实际情况下,ViewModel有更多的依赖关系 - 每个依赖关系都有自己的依赖关系(依此类推)。我使用的是Unity 1.2(尽管我认为这个问题在其他IoC容器中是相关的),而且我正在使用Caliburn的视图策略来自动查找并将适当的View连接到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)); 
    } 
} 

满足Func<IAddress, IAddressViewModel>接口的工厂方法已在主要UnityContainer中注册。工厂方法使用一个子容器注册由视图模型所需的IAddress依赖,然后解决了孩子视图模型:

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

现在,当PersonViewModel被初始化,它遍历示范,并呼吁各AddressCreateAddressViewModel()(通过参数Func<IAddress, IAddressViewModel>注入)。 CreateAddressViewModel()创建一个临时子容器并注册IAddress模型,以便当它解析子容器中的IAddressViewModel时,AddressViewModel通过其构造函数注入正确的实例。

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

  • 这是最好的方式注入子模型到子ViewModels与统一?
  • 有没有更好的(或更快)的方式去做其他IoC容器,例如Autofac?
  • 怎么会这个问题,MEF加以解决,因为它不是一个传统的IoC容器,但仍用于合成的对象?

回答

2

取决于容器,你不能在你的工厂的CreateAddressViewModel方法中指定一个参数(命名或以其他方式)?

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

根据不同的容器上你的工厂可能需要知道参数的名称(TinyIoC和Castle据我所知),也可能必须是最后一个在构造函数依赖(取决于容器因人而异),列表中哪些虽然不是很好,但它可以快速连续地创建大量的子容器,以及后续的GC抖动,并且您仍然可以获得用于所有其他依赖项的DI。

当然,这倒下,如果你的虚拟机也有需要相同IAddress,在这种情况下,一个子容器可能是要走,除非你想要的VM有容器的知识的方式的依赖。

更新: 如果您使用的是使用“最后一个寄存器胜”(我认为统一所做的)容器的子容器,那么你可以每次都通过相同孩子容器到你的工厂,并有你的工厂只需注册新的IAddress - 这样你就不会在每次迭代时在堆上创建一个新的UnityContainer实例,并且如果你正在创建大量项目,它应该减少垃圾收集。

+0

正如你指出的指定参数是否有任何相关性需要的型号,这一直是我的一个搅局者不起作用。 虽然重新使用子容器是一种可能性。 – GraemeF 2010-05-10 16:18:53

0

WPF Application Framework (WAF)的视图模型示例应用程序展示了如何把模型和视图模型在一起。该示例使用MEF作为依赖注入框架。

+0

翻翻我没有任何地方,这是否发现样本 - 他们倾向于建立一个单一的视图模型,并在其上设置模型作为选择的变化。也许我没有找到正确的地方,你可以指点我的想法吗? – GraemeF 2010-05-15 13:23:06

相关问题