我在我的 WPF 应用程序中使用构造函数依赖注入(inject),并且一直遇到以下模式,因此希望获得其他人的意见并了解替代解决方案。
目标是将 ViewModel 的层次结构连接到相似的模型层次结构,以便在每个模型中呈现信息的责任在于其自己的 ViewModel 实现。 (这种模式在其他情况下也会出现,但 MVVM 应该是一个很好的例子。)
这是一个简化的例子。鉴于我有一个包含更多模型集合的模型:
public interface IPerson
{
IEnumerable<IAddress> Addresses { get; }
}
public interface IAddress
{
}
我想在 ViewModels 中镜像这个层次结构,以便我可以将 ListBox(或其他)绑定(bind)到 Person ViewModel 中的集合:
public interface IPersonViewModel
{
ObservableCollection<IAddressViewModel> Addresses { get; }
void Initialize();
}
public interface IAddressViewModel
{
}
子 ViewModel 需要呈现来自子 Model 的信息,所以它是通过构造函数注入(inject)的:
public class AddressViewModel : IAddressViewModel
{
private readonly IAddress _address;
public AddressViewModel(IAddress address)
{
_address = address;
}
}
问题是,将子模型提供给相应的子 ViewModel 的最佳方式是什么?
这个例子很简单,但在典型的真实案例中,ViewModel 有更多的依赖关系——每个都有自己的依赖关系(等等)。我正在使用 Unity 1.2(尽管我认为这个问题与其他 IoC 容器相关),并且我正在使用 Caliburn 的 View 策略来自动查找适当的 View 并将其连接到 ViewModel。
这是我目前的解决方案:
父 ViewModel 需要为每个子 Model 创建一个子 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>
的工厂方法接口(interface)注册到主UnityContainer
.工厂方法使用子容器注册 IAddress
ViewModel 所需的依赖项,然后解析子 ViewModel: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
被初始化,它遍历每个 Address
在模型中并调用 CreateAddressViewModel()
(通过 Func<IAddress, IAddressViewModel>
参数注入(inject))。 CreateAddressViewModel()
创建一个临时子容器并注册 IAddress
模型,以便当它解析 IAddressViewModel
从子容器 AddressViewModel
获取通过其构造函数注入(inject)的正确实例。这对我来说似乎是一个很好的解决方案,因为 ViewModel 的依赖关系非常清晰,它们很容易测试并且不知道 IoC 容器。另一方面,性能还可以,但不是很好,因为可以创建很多临时子容器。此外,我最终得到了很多非常相似的工厂方法。
最佳答案
根据容器,您可以不在工厂的 CreateAddressViewModel 方法中指定参数(命名或其他)吗?
container.Resolve<IAddressViewModel>(new NamedParameterOverloads() { { "Address", model } };
根据容器,您的工厂可能必须知道参数的名称(TinyIoC 和 Castle afaik),或者它可能必须在构造函数依赖项列表中位于最后(YMMV 取决于容器),这不是很好,但它节省了快速连续创建大量子容器,以及随之而来的 GC 抖动,并且您仍然可以获得所有其他依赖项的 DI。
当然,如果您的 VM 还具有需要相同 IAddress 的依赖项,则这种情况会下降,在这种情况下,除非您希望 VM 了解容器,否则子容器可能是可行的方法。
更新:
如果您正在使用使用“最后注册获胜”的容器的子容器(我认为 Unity 会这样做),那么您可以每次将同一个子容器传递到您的工厂,并让您的工厂简单地注册新的 IAddress - 那这样您就不会在每次迭代的堆上创建一个新的 UnityContainer 实例,并且如果您要创建大量项目,它应该减少垃圾收集。
关于mvvm - 如何使用构造函数依赖注入(inject)将模型从集合提供给他们的 ViewModel?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/2803376/