8

Estoy acostumbrado a IoC/DI en aplicaciones web, principalmente Ninject con MVC3. Mi controlador está creado para mí, rellenado con todas las dependencias en su lugar, subdependencias, etc.La forma correcta de realizar la inyección de dependencias en un cliente de Windows (WPF) Aplicación

Sin embargo, las cosas son diferentes en una aplicación cliente gruesa. Tengo que crear mis propios objetos, o tengo que volver a un enfoque de estilo de localizador de servicios donde pido al kernel (probablemente a través de alguna interfaz, para permitir la capacidad de prueba) que me proporcione un objeto completo con dependencias.

Sin embargo, he visto varios lugares que el Localizador de servicios ha sido descrito como un antipatrón.

Así que mi pregunta es: si quiero beneficiarme de Ninject en mi aplicación cliente grueso, ¿hay una forma mejor/más adecuada para obtener todo esto?

  • Comprobabilidad
  • DI apropiado/IOC
  • La menor cantidad de acoplamiento posible

Tenga en cuenta que no estoy hablando sólo de MVVM y trasladarse en modelos de vista en las vistas. Esto se desencadena específicamente por la necesidad de proporcionar un objeto tipo repositorio desde el kernel, y luego las entidades recuperadas de ese repositorio reciben funcionalidad (los datos provienen de la base de datos, pero también necesitan algunos objetos como parámetros dependiendo del estado del mundo, y Ninject sabe cómo proporcionar eso). ¿De alguna manera puedo hacer esto sin dejar a los repositorios y entidades como un desastre incontrastable?

Si algo no está claro, házmelo saber. ¡Gracias!

EDICIÓN DE JULIO DE decimocuarto

estoy seguro de que las dos respuestas proporcionadas son probablemente correcta. Sin embargo, cada fibra de mi cuerpo está luchando contra este cambio; Parte de esto probablemente se deba a una falta de conocimiento, pero también hay una razón concreta por la que tengo problemas para ver la elegancia de esta forma de hacer las cosas;

No expliqué esto lo suficientemente bien en la pregunta original, pero el hecho es que estoy escribiendo una biblioteca que será utilizada por varias (4-5 al principio, quizás más adelante) aplicaciones de cliente de WPF. Todas estas aplicaciones operan en el mismo modelo de dominio, etc., por lo que mantenerlo todo en una sola biblioteca es la única manera de mantenerse SECO. Sin embargo, también existe la posibilidad de que los clientes de este sistema escriban sus propios clientes, y quiero que tengan una biblioteca simple y limpia para hablar. No quiero obligarlos a usar DI en su Composition Root (usando el término como Mark Seeman en su libro), porque eso dificulta enormemente las cosas en comparación con ellos simplemente al actualizar MyCrazySystemAdapter() y usar eso.

Ahora, el MyCrazySystemAdapter (nombre elegido porque sé que la gente no estará de acuerdo conmigo aquí) debe estar compuesto por subcomponentes, y ensamblado mediante DI. MyCrazySystemAdapter en sí no debería necesitar ser inyectado. Es la única interfaz que los clientes deben usar para hablar con el sistema. Entonces, un cliente felizmente debería obtener uno de esos, DI sucede como magia detrás de escena, y el objeto está compuesto por muchos objetos diferentes utilizando las mejores prácticas y principios.

Me doy cuenta de que esta va a ser una forma controvertida de querer hacer cosas. Sin embargo, también conozco a las personas que van a ser clientes de esta API.Si ven que necesitan aprender y cablear un sistema DI, y crear su estructura de objetos completa antes de tiempo en su punto de entrada de la aplicación (Composition Root), en lugar de reactivar un solo objeto, me darán el dedo medio y meterse en la base de datos directamente y arruinar las cosas de una forma difícil de imaginar.

TL; DR: La entrega de una API correctamente estructurada es demasiada molestia para el cliente. Mi API necesita entregar un solo objeto, construido detrás de escena usando DI y prácticas adecuadas, que puedan usar. El mundo real algunas veces supera el deseo de construir todo al revés para mantenerse fiel a los patrones y las prácticas.

Respuesta

5

puedo sugerir a echar un vistazo a los marcos MVVM como Caliburn. Proporcionan integración con contenedores IoC.


Básicamente, debe compilar la aplicación completa en su app.xaml. Si es necesario crear algunas partes más tarde porque aún no sabe todo para crearlas al inicio, inyecte una fábrica como interfaz (consulte a continuación) o Func (consulte Does Ninject support Func (auto generated factory)?) en la clase que necesita para crear esta instancia. Ambos serán compatibles de forma nativa en el próximo lanzamiento de Ninject.

p. Ej.

public interface IFooFactory { IFoo CreateFoo(); } 
public class FooFactory : IFooFactory 
{ 
    private IKernel kernel; 
    FooFactory(IKernel kernel) 
    { 
     this.kernel = kernel; 
    } 

    public IFoo CreateFoo() 
    { 
     this.kernel.Get<IFoo>(); 
    } 
} 

Tenga en cuenta que la implementación de fábrica pertenece lógicamente a la configuración del contenedor y no a la implementación de sus clases de negocio.

1

No sé nada sobre WPF o MVVM, pero su pregunta es básicamente acerca de cómo sacar cosas del contenedor sin utilizar un localizador de servicio (o el contenedor directamente) por todas partes, ¿verdad?
En caso afirmativo, puedo mostrar un ejemplo.

El punto es que utiliza una fábrica en su lugar, que utiliza el contenedor internamente. De esta forma, en realidad estás usando el contenedor en un solo lugar.

Nota: Voy a utilizar un ejemplo con Windows Forms y no atado a un contenedor específico (ya que, como he dicho, no sé ... WPF y utilizo el castillo de Windsor en lugar de ninject), pero ya que su pregunta básica no está específicamente relacionada con WPF/NInject, le resultará fácil "portar" mi respuesta a WFP/NInject.

La fábrica tiene el siguiente aspecto:

public class Factory : IFactory 
{ 
    private readonly IContainer container; 

    public Factory(IContainer container) 
    { 
     this.container = container; 
    } 

    public T GetStuff<T>() 
    { 
     return (T)container.Resolve<T>(); 
    } 
} 

El formulario principal de la aplicación obtiene esta fábrica a través de la inyección de constructor:

public partial class MainForm : Form 
{ 
    private readonly IFactory factory; 

    public MainForm(IFactory factory) 
    { 
     this.factory = factory; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 
    } 
} 

El recipiente se inicializa cuando se inicia la aplicación, y la principal la forma se resuelve (por lo que obtiene la fábrica a través de la inyección del constructor).

static class Program 
{ 
    static void Main() 
    { 
     var container = new Container(); 
     container.Register<MainForm>(); 
     container.Register<IFactory, Factory>(); 
     container.Register<IYourRepository, YourRepository>(); 

     Application.Run(container.Resolve<MainForm>()); 
    } 
} 

Ahora la forma principal puede utilizar la fábrica para conseguir cosas como su repositorio fuera del contenedor:

var repo = this.factory.GetStuff<IYourRepository>(); 
repo.DoStuff(); 

Si usted tiene más formas y desea utilizar la fábrica a partir de ahí, así, solo necesita inyectar la fábrica en estos formularios, como en el formulario principal, registrar los formularios adicionales al inicio también y abrirlos desde el formulario principal con la fábrica.

¿Esto es lo que quería saber?


EDIT:
Ruben, por supuesto, tienes razón. Mi error.
Todo el material en mi respuesta era un viejo ejemplo que tenía en algún lado, pero tenía prisa cuando publiqué mi respuesta y no leí el contexto de mi viejo ejemplo con la suficiente atención.

Mi viejo ejemplo incluía tener un formulario principal, desde el que puede abrir cualquier otra forma de la aplicación. Eso es para lo que era la fábrica, por lo que no tiene que inyectar cada otra forma mediante la inyección del constructor en el formulario principal.
su lugar, puede utilizar la fábrica para abrir cualquier nueva forma:

var form = this.factory.GetStuff<IAnotherForm>(); 
form.Show(); 

Por supuesto que no es necesario la fábrica sólo para obtener el repositorio de un formulario, siempre que el repositorio se pasa a la forma mediante inyección de constructor
Si su aplicación consiste en sólo unas pocas formas, no es necesario la fábrica en absoluto, que sólo puede pasar a las formas a través de la inyección de constructor, así:

public partial class MainForm : Form 
{ 
    private readonly IAnotherForm form; 

    // pass AnotherForm via constructor injection 
    public MainForm(IAnotherForm form) 
    { 
     this.form = form; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 
    } 

    // open AnotherForm 
    private void Button1_Click(object sender, EventArgs e) 
    { 
     this.form.Show(); 
    } 
} 

public partial class AnotherForm : Form 
{ 
    private readonly IRepository repo; 

    // pass the repository via constructor injection 
    public AnotherForm(IRepository repo) 
    { 
     this.repo= repo; 
     InitializeComponent(); // or whatever needs to be done in a WPF form 

     // use the repository 
     this.repo.DoStuff(); 
    } 
} 
+4

-1 Your 'GetStuff()' es un localizador de servicio en un vestido. No hagas esto (No hay nada de malo con el resto de su respuesta, pero el SLness lo hace iremanente) –

+0

Ruben, tienes razón. ¡Corregí mi respuesta! –

+0

Incluso antes de ver la respuesta de Remo (que creo que es el enfoque correcto), iba a sugerir la inyección de fábricas (como Funcs en su caso a la http://stackoverflow.com/questions/4840157/does-ninject-support- func-auto-generated-factory/4851885 # 4851885) –

Cuestiones relacionadas