当使用依赖注入(DI)和控制反转(IoC)时,对象通常会有一个构造函数,它接受对象正常运行所需的依赖集。
例如,如果我有一个需要服务来填充组合框的表单,您可能会看到如下内容:
// my files
public interface IDataService {
IList<MyData> GetData();
}
public interface IComboDataService {
IList<MyComboData> GetComboData();
}
public partial class PopulatedForm : BaseForm {
private IDataService service;
public PopulatedForm(IDataService service) {
//...
InitializeComponent();
}
}
这在顶级工作正常,我只是使用我的IoC容器来解决依赖关系:
var form = ioc.Resolve<PopulatedForm>();
但面对生成的代码,这会变得更难。在winforms中,生成组成其余部分类的第二个文件。此文件引用其他组件(如自定义控件),并使用no-args构造函数创建此类控件:
// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
private void InitializeComponent() {
this.customComboBox = new UserCreatedComboBox();
// customComboBox has an IComboDataService dependency
}
}
由于这是生成的代码,我无法传递依赖关系,并且没有简单的方法让我的IoC容器自动注入所有依赖项。
一种解决方案是将每个子组件的依赖关系传递给PopulatedForm
,即使它可能不需要它们,例如IComboDataService
所需的UserCreatedComboBox
。然后,我有责任确保通过各种属性或setter方法提供依赖项。然后,我的PopulatedForm
构造函数可能如下所示:
public PopulatedForm(IDataService service, IComboDataService comboDataService) {
this.service = service;
InitializeComponent();
this.customComboBox.ComboDataService = comboDataService;
}
另一种可能的解决方案是让no-args构造函数执行必要的解决方案:
public class UserCreatedComboBox {
private IComboDataService comboDataService;
public UserCreatedComboBox() {
if (!DesignMode && IoC.Instance != null) {
comboDataService = Ioc.Instance.Resolve<IComboDataService>();
}
}
}
两种解决方案都不是特别好。在生成的代码面前,有哪些模式和替代方法可以更有效地处理依赖注入?我希望看到两种通用解决方案,例如模式,以及特定于C#,Winforms和Autofac的解决方案。
答案 0 :(得分:6)
我相信这里没有银弹解决方案。在这种情况下,我会使用属性注入来保留无参数构造函数。另外我个人不喜欢将服务注入到UI类中,我更喜欢在那里注入某种类型的Presenter。然后你有一个属性Presenter,它将由IoC容器设置,在这个属性的setter中你将有你的初始化代码。
在你的两个解决方案中,我不喜欢第二个解决方案,特别是因为在代码中引用了IoC容器,这是不好的IMO。
答案 1 :(得分:3)
我想说你的UI,尤其是UI的子元素,不需要提供任何服务。
很难判断这对你的应用程序有多可行,但MVC或MVP是为了避免这种需要。
我会尝试重新设计,以便控制器负责与服务交互,控制器为视图元素提供所需的一切,而不是让视图元素询问他们需要什么。