16

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.

Respuesta

6

Creo que no hay una solución a esta situación aquí. Yo usaría inyección de propiedad en este caso para dejar el constructor sin parámetros. Además, personalmente, no me gusta la inyección de servicios en las clases de UI, prefiero inyectar algún tipo de Presentadores allí. Luego tiene un presentador de propiedades que se configurará con el contenedor IoC y en el establecimiento de esta propiedad tendrá su código de inicialización.

Fuera de sus dos soluciones, no me gusta el segundo, especialmente porque hace referencia al contenedor IoC en su código que es malo IMO.

+0

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. –

3

Diría que su UI, especialmente los subelementos de su UI, no deberían necesitar ningún servicio.

Es difícil juzgar qué tan factible es esto para su aplicación, pero MVC o MVP está destinado a evitar esta necesidad.

Intentaré rediseñar para que un controlador sea responsable de interactuar con los servicios y ese controlador le da a los elementos de vista todo lo que necesitan, en lugar de dejar que los elementos de vista soliciten lo que necesitan.

Cuestiones relacionadas