17

Después de leer más y más sobre los contenedores de IoC, leo this post acerca de no tener IoC.Resolve() etc en su código.Eliminar dependencia en el Contenedor de IoC

Tengo mucha curiosidad por saber, ¿cómo puedo eliminar la dependencia del contenedor?

me quiero escribir código como el siguiente:

public void Action() 
{ 
    using(IDataContext dc = IoC.Resolve<IDataContext>()) 
    { 
     IUserRepository repo = IoC.Resolve<IUserRepository>(); 
     // Do stuff with repo... 
    } 
} 

Pero, ¿cómo puedo deshacerme de la IoC.Resolve llamadas? Tal vez necesito una mejor comprensión de DI ...

Gracias de antemano.

Respuesta

17

En general, la mayoría de las dependencias se pueden inyectar en su clase en el momento de su creación. Sin embargo, en este caso particular, necesita un componente que debe crearse bajo demanda en el momento del uso. En tales casos, es muy difícil eliminar por completo la dependencia de un contenedor IoC. Mi enfoque siempre ha sido crear una fábrica que se inyecte en la clase en el momento de la creación, que a su vez encapsula todo el uso directo de la IoC. Esto permite a sus fábricas, para burlarse de las pruebas, en lugar del contenedor de IoC sí ... que tiende a ser mucho más fácil:

// In Presentation.csproj 
class PresentationController 
{ 
    public PresentationController(IDataContextFactory dataContextFactory, IRepositoryFactory repositoryFactory) 
    { 
     #region .NET 4 Contract 
     Contract.Requires(dataContextFactory != null); 
     Contract.Requires(repositoryFactory != null); 
     #endregion 

     _dataContextFactory = dataContextFactory; 
     _repositoryFactory = repositoryFactory; 
    } 

    private readonly IDataContextFactory _dataContextFactory; 
    private readonly IRepositoryFactory _repositoryFactory; 

    public void Action() 
    { 
     using (IDataContext dc = _dataContextFactory.CreateInstance()) 
     { 
      var repo = _repositoryFactory.CreateUserRepository(); 
      // do stuff with repo... 
     } 
    } 
} 

// In Factories.API.csproj 
interface IDataContextFactory 
{ 
    IDataContext CreateInstance(); 
} 

interface IRepositoryFactory 
{ 
    IUserRepository CreateUserRepository(); 
    IAddressRepository CreateAddressRepository(); 
    // etc. 
} 

// In Factories.Impl.csproj 
class DataContextFactory: IDataContextFactory 
{ 
    public IDataContext CreateInstance() 
    { 
     var context = IoC.Resolve<IDataContext>(); 
     // Do any common setup or initialization that may be required on 'context' 
     return context; 
    } 
} 

class RepositoryFactory: IRepositoryFactory 
{ 
    public IUserRepository CreateUserRepository() 
    { 
     var repo = IoC.Resolve<IUserRepository>(); 
     // Do any common setup or initialization that may be required on 'repo' 
     return repo; 
    } 

    public IAddressRepository CreateAddressRepository() 
    { 
     var repo = IoC.Resolve<IAddressRepository>(); 
     // Do any common setup or initialization that may be required on 'repo' 
     return repo; 
    } 

    // etc. 
} 

La ventaja de este enfoque es que, mientras que no se puede eliminar por completo la dependencia de la COI en sí mismo, puede encapsularlo en un único tipo de objeto (una fábrica), desacoplando la mayor parte de su código del contenedor IoC. Esto mejora la agilidad de los códigos a la luz de, por ejemplo, el cambio de un contenedor de IoC a otro (es decir, Windsor a Ninject).

Cabe señalar, una consecuencia interesante de esto, es que sus fábricas generalmente se inyectan a sus dependientes por el mismo marco de IoC que utilizan. Si está utilizando Castle Windsor, por ejemplo, crearía una configuración que le diga al contenedor IoC que inyecte las dos fábricas en su componente comercial cuando se cree. El componente comercial en sí mismo también puede tener una fábrica ... o, simplemente, puede ser inyectado por el mismo marco de trabajo de IoC en un componente de nivel superior, etc., etc., ad inf.

+0

Gracias por la respuesta. El único problema es que el IoC que estoy usando debe tener un alcance relativo a una instrucción 'using'. Luego, si resuelvo, diga 'IDataContext' y se resolverá la * instancia única * para ese ámbito particular. No quiero que mis controladores, etc., estén al tanto de un contenedor IoC, pero ¿hay realmente alguna forma de evitarlo? – TheCloudlessSky

+0

Lo que me pregunto es si un Controlador debería ser capaz de llamar a IoC.Resolve ? Si no, ¿quién debería hacer esta llamada? – TheCloudlessSky

+0

¿Podría explicar un poco más sobre este alcance? ¿Qué contenedor de IoC estás usando? En términos generales, acoplar cualquiera de sus códigos al marco del contenedor de cualquier manera es un tipo negativo de acoplamiento ... debe evitarlo a toda costa. En mi experiencia, rara vez, o nunca, se requiere un alcance (o contexto) para que funcione un contenedor IoC. Si funciona de esa manera, encontraría un contenedor alternativo o encontraría una forma de proporcionar ese contexto a las fábricas que resuelvan sus objetos y mantendrán su estructura de IoC desacoplada tanto como sea posible. – jrista

1

Disponga de un segundo inyector de dependencia para inyectar el primero y pida al primero que inyecte el segundo.

+0

Usted Puede que se ría, pero he visto a personas abogar por esto, aparentemente en seriedad. – mschaef

2

Estaba en un proyecto hace un tiempo que no se había establecido en un contenedor IoC. Manejaron la incertidumbre al rechazar las características no específicas de IoC de su contenedor y también envolviendo Resolve con su propia clase. Esto también es algo que he visto defendido algunas veces en publicaciones de blogs ... eliminar la última dependencia, la dependencia del contenedor de inyección de dependencia.

Esta es una técnica práctica, pero en algún momento debe elegir las herramientas que usa y aceptar que pagará los costos para cambiar a herramientas alternativas. Para mí, los contenedores de IoC entran en la categoría de cosas que probablemente deberías abrazar de todo corazón, así que cuestiono este nivel de factorización. Si usted quiere ver más en esto, sugiero el siguiente enlace:

http://blog.objectmentor.com/articles/2010/01/17/dependency-injection-inversion

2

Una alternativa es volver a escribir el método de aceptar Func<T> delegados.Esto elimina la dependencia del método y que permite a la unidad de prueba con una maqueta:

public void Action(Func<IDataContext> getDataContext, Func<IUserRepository> getUserRepository) 
{ 
    using(IDataContext dc = getDataContext()) 
    { 
     IUserRepository repo = getUserRepository(); 
     // Do stuff with repo... 
    } 
} 
2

que escribió sobre esta misma cuestión recientemente:

+0

Gracias por los enlaces. Entonces, todo se reduce a usar una fábrica. Y por lo que leí, para crear una instancia de la fábrica, todavía tiene que tener una referencia al contenedor IoC, ¿correcto? – TheCloudlessSky

+0

No, no necesita ningún tipo de referencia al contenedor para obtener la fábrica, ese es el punto. –

+0

¿Ok entonces la fábrica tiene la referencia al contenedor entonces? ¿Dónde vive la fábrica? Entiendo cómo se crea la fábrica, no estoy seguro de dónde ponerla. – TheCloudlessSky

Cuestiones relacionadas