面对winforms等生成代码的IoC/DI
使用依赖项注入 (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 中,生成了组成部分类其余部分的第二个文件。该文件引用其他组件,例如自定义控件,并使用无参数构造函数来创建此类控件:
// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
private void InitializeComponent() {
this.customComboBox = new UserCreatedComboBox();
// customComboBox has an IComboDataService dependency
}
}
由于这是生成的代码,因此我无法传递依赖项,并且没有简单的方法可以让我的 IoC 容器自动注入所有依赖关系。
一种解决方案是将每个子组件的依赖项传递给 PopulatedForm
,即使它可能并不直接需要它们,例如使用 UserCreatedComboBox< 所需的
IComboDataService
/代码>。然后,我有责任确保通过各种属性或 setter 方法提供依赖项。然后,我的 PopulatedForm
构造函数可能如下所示:
public PopulatedForm(IDataService service, IComboDataService comboDataService) {
this.service = service;
InitializeComponent();
this.customComboBox.ComboDataService = comboDataService;
}
另一种可能的解决方案是使用无参数构造函数来执行必要的解析:
public class UserCreatedComboBox {
private IComboDataService comboDataService;
public UserCreatedComboBox() {
if (!DesignMode && IoC.Instance != null) {
comboDataService = Ioc.Instance.Resolve<IComboDataService>();
}
}
}
这两种解决方案都不是特别好。面对生成的代码,有哪些模式和替代方案可以更有效地处理依赖注入?我希望看到通用解决方案(例如模式)以及特定于 C#、Winforms 和 Autofac 的解决方案。
When using dependency injection (DI) and inversion of control (IoC) objects will typically have a constructor that accepts the set of dependencies required for the object to function properly.
For example, if I have a form that requires a service to populate a combo box you might see something like this:
// 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();
}
}
This works fine at the top level, I just use my IoC container to resolve the dependencies:
var form = ioc.Resolve<PopulatedForm>();
But in the face of generated code, this gets harder. In winforms a second file composing the rest of the partial class is generated. This file references other components, such as custom controls, and uses no-args constructors to create such controls:
// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
private void InitializeComponent() {
this.customComboBox = new UserCreatedComboBox();
// customComboBox has an IComboDataService dependency
}
}
Since this is generated code, I can't pass in the dependencies and there's no easy way to have my IoC container automatically inject all the dependencies.
One solution is to pass in the dependencies of each child component to PopulatedForm
even though it may not need them directly, such as with the IComboDataService
required by the UserCreatedComboBox
. I then have the responsibility to make sure that the dependencies are provided through various properties or setter methods. Then, my PopulatedForm
constructor might look as follows:
public PopulatedForm(IDataService service, IComboDataService comboDataService) {
this.service = service;
InitializeComponent();
this.customComboBox.ComboDataService = comboDataService;
}
Another possible solution is to have the no-args constructor to do the necessary resolution:
public class UserCreatedComboBox {
private IComboDataService comboDataService;
public UserCreatedComboBox() {
if (!DesignMode && IoC.Instance != null) {
comboDataService = Ioc.Instance.Resolve<IComboDataService>();
}
}
}
Neither solution is particularly good. What patterns and alternatives are available to more capably handle dependency-injection in the face of generated code? I'd love to see both general solutions, such as patterns, and ones specific to C#, Winforms, and Autofac.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
我相信这里没有灵丹妙药。在这种情况下,我将使用属性注入来保留无参数构造函数。另外,我个人不喜欢将服务注入到 UI 类中,我更喜欢在那里注入某种 Presenter。然后你有一个属性 Presenter,它将由 IoC 容器设置,并且在该属性的 setter 中你将拥有初始化代码。
在你的两个解决方案中,我不喜欢第二个,特别是因为在你的代码中引用了 IoC 容器,这在我看来是很糟糕的。
I believe there is no silver-bullet solution here. I would use property injection in this case to leave parameterless constructor. Also I personally do not like injection of services into UI classes, I prefer injecting some kind of Presenters there. Then you have a property Presenter which will be set by IoC container and in setter of this property you will have your initializing code.
Out of your two solutions I do not like second one especially because of referencing IoC container in your code which is bad IMO.
我想说的是,您的 UI,尤其是 UI 的子元素,不需要提供任何服务。
很难判断这对于您的应用程序来说有多可行,但 MVC 或 MVP 旨在避免这种需求。
我会尝试重新设计,以便控制器负责与服务交互,并且控制器为视图元素提供它们需要的一切,而不是让视图元素询问它们需要什么。
I would say that your UI, especially sub-elements of your UI, shouldn't need to be provided with any services.
It's hard to judge how feasible this is for your app, but MVC or MVP is meant to avoid this need.
I would try to redesign so that a controller is responsible for interacting with services and that controller gives the view elements everything they need, rather than letting the view elements ask for what they need.