Cuando se utiliza inyección de dependencia (DI) e inversión de control (IoC), los objetos suelen tener un constructor que acepta el conjunto de dependencias necesarias para que el objeto funcione correctamente.IoC/DI frente a winforms y otros códigos generados
Por ejemplo, si tengo una forma que requiere un servicio para llenar un cuadro combinado se puede ver algo como esto:
// 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();
}
}
Esto funciona bien en el nivel superior, sólo tiene que utilizar mi contenedor IoC a resolver las dependencias:
var form = ioc.Resolve<PopulatedForm>();
Pero a la vista del código generado, esto se vuelve más difícil. En winforms se genera un segundo archivo que compone el resto de la clase parcial. Este archivo hace referencia a otros componentes, tales como controles personalizados, y utiliza sin argumentos constructores para crear este tipo de controles:
// generated file: PopulatedForm.Designer.cs
public partial class PopulatedForm {
private void InitializeComponent() {
this.customComboBox = new UserCreatedComboBox();
// customComboBox has an IComboDataService dependency
}
}
Dado que este se genera el código, no puedo pasar en las dependencias y no hay manera fácil de tener mi contenedor de IoC inyecta automáticamente todas las dependencias.
Una solución es pasar las dependencias de cada componente secundario a PopulatedForm
aunque no las necesite directamente, como con el IComboDataService
requerido por UserCreatedComboBox
. Entonces, tengo la responsabilidad de asegurarme de que las dependencias se proporcionan a través de varias propiedades o métodos setter. Entonces, mi PopulatedForm
constructor puede tener un aspecto como sigue:
public PopulatedForm(IDataService service, IComboDataService comboDataService) {
this.service = service;
InitializeComponent();
this.customComboBox.ComboDataService = comboDataService;
}
Otra posible solución es hacer que el constructor sin argumentos para hacer la resolución necesaria:
public class UserCreatedComboBox {
private IComboDataService comboDataService;
public UserCreatedComboBox() {
if (!DesignMode && IoC.Instance != null) {
comboDataService = Ioc.Instance.Resolve<IComboDataService>();
}
}
}
Ni solución es particularmente buena. ¿Qué patrones y alternativas están disponibles para manejar con mayor capacidad la inyección de dependencia frente al código generado? Me encantaría ver soluciones generales, como patrones, y específicas de C#, Winforms y Autofac.
Re: haciendo referencia al contenedor IoC - Estoy totalmente de acuerdo. Empiezo a pensar que, como usted alude, un diseño que requiere mucho más que un modelo de presentación dentro del código UI sufrirá en la capacidad de prueba y una buena separación de las preocupaciones. –