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