2012-09-10 8 views
17

Actualmente estoy usando el siguiente código:¿Qué ocurre con el uso de la declaración cuando me traslado a la inyección de dependencias

public class MyProvider 
{ 
    public MyProvider() 
    { 
    } 

    public void Fetch() 
    { 
     using (PopClient popClient = new PopClient()) 
     { 
      .... 
     } 
    } 
} 

Porque yo quiero ser capaz de probar la unidad del método de Fetch y debido al hecho de que lo que pueda' Para simular PopClient, creé una interfaz y una clase contenedora que llama a PopClient. Mi código actualizado es así:

public class MyProvider 
{ 
    private readonly IPopClient popClient; 

    public MyProvider(IPopClient popClient) 
    { 
     this.popClient = popClient; 
    } 

    public void Fetch() 
    { 
     using (var pop3 = popClient) 
     { 
      .... 
     } 
    } 
} 

estoy usando Ninject para la inyección de dependencias y no estoy muy seguro de qué tipo de efecto de la instrucción using tendrá en el código actualizado desde Ninject ya se crea una instancia de popclient y la inyectó en el constructor

¿La instrucción using descartará el objeto pop3 y dejará el objeto popClient solo para que Ninject pueda manejarlo o la sentencia using interferirá con Ninject?

¿Cuál es el enfoque adecuado en este caso? Cualquier idea sería muy útil.

+0

Si dispone de popClient, ¿qué ocurre con el objeto MyProvider? ¿No debería eliminarse también? Porque queda un solo miembro de readonly dispuesto en la clase. Si es así, creo que es mejor poner un método de disposición para la clase MyProvider. –

Respuesta

14

La variable pop3 se le dará la misma referencia a un IPopClient objeto que popClient tiene, así que cuando la declaración using es más, el objeto referido por tanto las variables locales y de instancia será Dispose() d, probablemente colocándolo en un estado inconsistente para un uso posterior.

Si desea utilizar varias instancias de IPopClient, uno por Fetch() llamada, lo que debe hacer es inyectar un "método de fábrica":

public class MyProvider 
{ 
    private readonly Func<IPopClient> createPopClient; 

    public MyProvider(Func<IPopClient> popClientFactory) 
    { 
     this.createPopClient = popClientFactory; 
    } 

    public void Fetch() 
    { 
     using (var pop3 = createPopClient()) 
     { 
      .... 
     } 
    } 
} 

Ahora, cuando se llama Fetch(), se ejecutará la fábrica método que devolverá una nueva referencia a IPopClient, que puede usarse y luego eliminarse sin afectar a ninguna otra llamada a ese método.

autofac apoya la inyección de métodos de fábrica para los tipos registrados sin ninguna configuración adicional (de ahí su nombre, creo); Creo que al configurar un contenedor Ninject es necesario que registre explícitamente un "getter" como método de fábrica para un tipo de devolución dado (que puede ser tan simple como un lambda ()=>new PopClient() o puede usar una llamada al método de resolución del contenedor).

+1

mejor para crear una interfaz de fábrica. La intención es más clara. +1 en ambos sentidos para el patrón de fábrica – jgauffin

+0

Siempre que tenga 'Ninject.Extensions.Factory.dll' en su AppDomain.BaseDirectory, Func se autogenera; consulte [la wiki' Ninject.Extensions.Factory'] (https: // github.com/ninject/ninject.extensions.factory/wiki/Func) (es decir, no es necesario registrar nada) –

+0

Voy a poner en segundo lugar el comentario hecho por @jgauffin arriba. Una interfaz de fábrica requiere un poco más de trabajo, pero valdrá la pena a largo plazo. La intención será más clara, y la burla también es más fácil. – Pflugs

1

Al configurar sus fijaciones, declarar el alcance:

https://github.com/ninject/ninject/wiki/Object-Scopes

Ninject llamará a disponer de los objetos que ha creado para ti, así que asegúrese de escribir hasta sus métodos de eliminación debe realizarse en cualquier objeto que usted da a Ninject para manejar.

+0

El problema es que con la configuración actual, MyProvider solo obtiene una instancia de un objeto IPopClient para usar; eso significa que si el código realiza múltiples llamadas a Fetch() usando una instancia de MyProvider, esto no tendrá efecto independientemente del alcance registrado de IPopClient. – KeithS

+0

Sí, el método Fetch se llamará varias veces, por lo que en el código actual (sin DI) siempre se me garantizó una nueva instancia pop debido a la instrucción using. En el código DI, creo que el alcance transitorio solo creará una instancia que será utilizada por múltiples llamadas y la declaración using dispondrá de esa instancia después de que se realice la primera llamada. – Thomas

+0

@Thomas - eso es exactamente correcto - ver mi respuesta.En resumen, si desea mantener la instrucción de uso, no desea inyectar una sola instancia, sino un método de fábrica al que puede llamar para producir tantas instancias como desee. – KeithS

Cuestiones relacionadas