16

当使用依赖注入(DI)和控制反转(IoC)对象时,通常会有一个构造函数接受该对象正常运行所需的一组依赖项。面对winforms和其他生成代码的IoC/DI

举例来说,如果我有需要服务来填充组合框的形式,你可能会看到这样的事情:

// 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的解决方案。

回答

6

我相信这里没有银子弹解决方案。在这种情况下,我会使用属性注入来留下无参数的构造函数。另外我个人不喜欢向UI类注入服务,我更喜欢在那里注入某种主持人。然后你有一个属性Presenter,它将由IoC容器设置,并且在这个属性的setter中,你将有你的初始化代码。

在你的两个解决方案中,我不喜欢第二个解决方案,特别是因为在你的代码中引用了IoC容器,这是不好的IMO。

+0

Re:引用IoC容器 - 我绝对同意。我开始认为,正如你所暗示的那样,在UI代码中需要远远超过演示模型的设计将会受到可测试性和良好的问题分离的困扰。 – 2011-02-11 23:16:53

3

我会说你的UI,尤其是你的UI的子元素,应该不需要提供任何服务。

很难判断这对您的应用程序有多可行,但MVC或MVP旨在避免此需求。

我会尝试重新设计,以便控制器负责与服务进行交互,并且该控制器给视图元素提供他们需要的所有内容,而不是让视图元素询问他们需要的内容。