我是DI的新手,我有一些问题,我希望人们可以为我清理,我目前正在研究使用Caliburn Micro的WPF-MVVM系统,使用MEF容器。
此应用程序用于跟踪货件并具有多个部分。我希望我能够清楚地解释这一点,但如果不清楚,请指出。
我有几个从数据库返回的实体(通过webservices)示例是shipments
,containers
,packages
。
对于这些实体中的每一个,我都有一个包含Web服务实体的模型,以及一个经理,管理者也负责通过webservices进行标准的CRUD操作在存储模型的ObservableCollection
时,会将这些管理器注入需要访问这些列表的viewmodels
构造函数。
所以,我有发货> shipmentManager > shipmentListViewModel ,这样做是为了允许多个viewmodels
使用相同的货件列表
但是,我已经开始遇到一些问题,其中一些viewmodels
包含构造函数,其中包含6个管理器,有些情况仅用于传递给新构造的{{1 }}
我希望有人可以为这个问题建议一个干净的解决方案,我想的是一个单独的课程,它将成为所有经理的容器,并且然后我可以简单地注入容器类并使用它来获得所需的经理,但是我看到有人建议反对该方法,而没有明确说明原因。
另外,另一个问题是,我的模型实施dialog viewmodels
,因为我的经理负责维护模型列表,并保存更改这些模型会在IEditableObject
内发布经理选择的事件吗?
编辑:所要求的代码:
引导程序创建并导出所需的类:
EndEdit
ContentViewModel处理菜单点击,这允许打开几个diaglogs,构造函数包含大量的DI:
protected override void Configure()
{
container = new CompositionContainer(new AggregateCatalog(AssemblySource.Instance.Select(x => new AssemblyCatalog(x)).OfType<ComposablePartCatalog>()));
CompositionBatch batch = new CompositionBatch();
IEventAggregator eventAggregator = new EventAggregator();
batch.AddExportedValue<IWindowManager>(new WindowManager());
batch.AddExportedValue<IEventAggregator>(eventAggregator);
batch.AddExportedValue<IManager<ShipmentContainer>>(new ContainerManager());
batch.AddExportedValue<IManager<Item>>(new ItemManager());
batch.AddExportedValue<IManager<OrderedItem>>(new OrderedItemManager());
batch.AddExportedValue<IManager<Package>>(new PackageManager());
batch.AddExportedValue<IManager<Proforma>>(new ProformaManager(eventAggregator));
batch.AddExportedValue<IManager<Project>>(new ProjectManager());
batch.AddExportedValue<IManager<Shipment>>(new ShipmentManager(eventAggregator));
batch.AddExportedValue<IManager<PackingItem>>(new PackingListManager(eventAggregator));
batch.AddExportedValue(container);
container.Compose(batch);
}
和对话框显示如下:
public LBLContentViewModel(IWindowManager windowManager, IManager<Project> pManager, IEventAggregator eventManager, IManager<Item> iManager, IManager<PackingItem> plManager, IManager<Shipment> sManager)
{
...
}
希望这是你想看到charleh的代码,如果不是,请告诉我,我会尝试提供所需的东西。
答案 0 :(得分:3)
对此有两点评论。可能不是你正在寻找的答案,但也许它可以响铃。
1.-我不会使用Manager对象。经理不是一个好词,它可能意味着什么,它可能会对许多事情负责,绝对不是一件好事,因为它会打破单一责任原则(SRP),SOLID principles之一。 / p>
2.-具有多个响应性的Manager可能不是一个好的方法,一个有6个依赖关系的类让我觉得这个类做得太多了。
显然,您可以使用依赖注入修复此问题,并忘记每次创建新对象的痛苦。但是,我认为它只会解决一个小问题,但不会解决主要问题。
我的建议是,你要对类及其许多依赖项进行分析,并尝试拆分,以便创建一些应用某些面向对象原则的责任较少的单元。
有时我们的课程会成长和增长,看起来任何事情都无法分割,尤其是ViewModels或Controllers。但是,View可以有多个控件和多个ViewModel ..也许这是可行的方法。
顺便说一句,this question can be helpful too!
编辑后:
我会像在评论中所说的那样做:
首先注册对话..
batch.AddExportedValue<ProjectSearchViewModel>(new ProjectSearchViewModel(eventAggregator,projectManager));
在主ViewModel中,您可以拥有:
public LBLContentViewModel(ProjectSearchViewModel projectSearchViewModel, OtherDependencies ...)
{
...
_projectSearchViewModel = projectSearchViewModel;
}
打开对话框:
public void OpenProject()
{
this._windowManager.ShowDialog(_projectSearchViewModel);
}
通过这种方式,您可以从MainViewModel中删除Dependencies,并将它们真正归属于对象。
在我看来,MEF是在大型应用程序中使用的强大库,能够使用许多程序集而无需耦合它们。它也可以用作依赖注入引擎,但我认为它不是为此目的而设计的。
Take a look at this great post about it。我建议添加一个IoC库,如Unity或Autofac,以更有效地执行依赖注入。
答案 1 :(得分:2)
我相信你的问题已经有了最好的答案:
我希望有人能为这个问题建议一个干净的解决方案, 我想的是一个单独的课程,它将成为所有人的容器 经理,然后我可以简单地注入该容器类并使用 那是为了得到理想的经理,但是我看到人们的建议 反对这种方法,没有明确说明原因。
在我看来,这是最好的答案。如果你发现你经常将一组相同的东西注入到你的构造函数中,那么很可能那组对象形成某种逻辑分组,如果组合成一个类就会有意义。
还要了解@margabit在回答分析类及其依赖关系时所说的内容。设计的不同结构可能首先避免了这种情况,但并不总是可以返回并改变一切。
答案 2 :(得分:2)
我在这里做了一些假设,但是:
是否有什么东西阻止您让对话框指定其依赖项并让容器解析这些依赖项?
听起来就像你的经理课程会是单身生活方式(如果我错了,请纠正我);如果是这种情况,导致显示对话框的VM不需要对他们真正不感兴趣的管理员依赖:对话本身应该在实例化时解决这些依赖关系(我的假设再一次是你的对话框)会是暂时的)
SRP声明对象应该只有一个责任;虽然听起来像是一个只负责包含更多类的类是一个单一的责任,你实际上只是创建了另一个容器,这是IoC容器已经在做的事情。
你能发布一些示例代码或澄清你如何设置IoC - 什么是单例,什么是瞬态?大多数IoC容器也有工厂选项(例如Castle Windsors TypedFactory),它可以让您更好地控制瞬态实例化,因此这可能是一个选项(取决于您的复杂性)
编辑:看到您的代码后,确定解决方案非常简单......
如果您尚未这样做,请在MEF中导出ViewModels
。
容器应该能够将所有VM解析为组件。不要担心出口数量。我不确定MEF是否支持基于约定的注册(快速搜索表明它确实如此,但我在多大程度上不确定)
导出虚拟机后,可以在需要虚拟机时让MEF实例化并满足依赖关系
当您需要VM的实例时,您可以使用CM中提供的静态IoC
类(它只是从容器中解析出来)
var vm = IoC.Get<ProjectSearchViewModel>()
现在在对话框中显示此VM
_windowManager.ShowDialog(vm);
您也可以更好地将ProjectSearchViewModel
的依赖项移动到主VM的构造函数中,这样可以更清晰(但无论哪种方式都可以)
由于在实例化时现在正满足依赖关系,因此对话框可以指定它所依赖的内容,而父窗口不需要知道
然后LBLContentViewModel
构造函数变得不那么臃肿了:
public LBLContentViewModel(IWindowManager windowManager, IEventAggregator eventManager)
{
...
}
ProjectSearchViewModel
正确指定了它的依赖关系
public ProjectSearchViewModel(IEventAggregator eventAggregator, IManager<Project> projectManager)
{
...
}
这是IoC的Inversion
部分 - 现在组件和子组件指定了它们所需要的和容器提供的内容。以前它是另一种方式,一个组件决定子组件需要什么......这将是一个痛苦的前进!
答案 3 :(得分:-2)
我建议使用任何DI(依赖注入)组件。它非常干净而且功能强大。
一些流行的DI组件: - Ninject - 团结 - Castle.Windsor - Autofac - StructureMap
否则,您可以定义一个属性,其类型是您有两个或更多类的接口类型。然后动态创建适当的对象并将其设置为属性。您可以在配置文件中存储类名和完全限定类名映射,这将在动态创建适当的对象时使用。