13

Estoy usando C# con el marco Unity de Microsoft. No estoy muy seguro de cómo resolver este problema. Probablemente tiene algo que ver con mi falta de comprensión de DI con Unity.Inyección de constructor en C#/Unidad?

Mi problema se puede resumir con el siguiente código de ejemplo:

class Train(Person p) { ... } 

class Bus(Person p) { ... } 

class Person(string name) { ... } 

Person dad = new Person("joe"); 
Person son = new Person("timmy"); 

Cuando llamo el método de determinación de autobús ¿Cómo puedo estar seguro de que se inyecta la Persona 'hijo' con el nombre 'Timmy' y al resolver Train, ¿cómo puedo estar seguro de que la Persona 'dad' con el nombre 'joe' está resuelta?

¿Estoy pensando en utilizar instancias con nombre? Pero estoy perdido. Cualquier ayuda sería apreciada.

Como un aparte, prefiero no crear una interfaz IPerson.

Respuesta

15

Una forma de resolver esto sería utilizar un constructor de inyección con un nombre de registro.

// Register timmy this way 
Person son = new Person("Timmy"); 
container.RegisterInstance<Person>("son", son); 

// OR register timmy this way 
container.RegisterType<Person>("son", new InjectionConstructor("Timmy")); 

// Either way, register bus this way. 
container.RegisterType<Bus>(new InjectionConstructor(container.Resolve<Person>("son"))); 

// Repeat for Joe/Train 
+2

¿cómo sería posible almacenar estos en el archivo de configuración en lugar de la codificación? –

32

A menos que se registra, respectivamente, "Joe" y "Timmy" como dependencias con nombre, no se puede estar seguro de que "Timmy" se inyecta en Schoolbus. De hecho, si intenta registrar dos instancias de la misma clase que las dependencias sin nombre, tendrá una configuración ambigua, y no podrá resolver el Person en absoluto.

En general, si tiene que registrar una gran cantidad de instancias con nombre, es probable que se encuentre con DI de forma incorrecta. La idea principal de DI es resolver Servicios de dominio más de Objetos de dominio.

La idea principal de DI es proporcionar un mecanismo que le permite resolver tipos abstractos (interfaces o clases abstractas) en tipos concretos. Su ejemplo no tiene tipos abstractos, por lo que realmente no tiene mucho sentido.

+0

Gracias por su respuesta perspicaz. Aunque puede que tengas razón, DI e IoC son conceptos nuevos para mí, así que todavía estoy tratando de descubrir el mejor diseño. –

+1

Eso es justo; quizás quieras leer mi libro, entonces :) –

+2

JP, echa un vistazo a su libro. He encontrado que es muy perspicaz. –

14

Mark Seeman lo hizo bien. Y simpatizo con tu confusión. Lo revisé yo mismo cuando aprendí a usar contenedores automáticos de inyección de dependencia. El problema es que hay muchas formas válidas y razonables de diseñar y usar objetos. Sin embargo, solo algunos de estos enfoques funcionan con contenedores de inyección de dependencia automática.

Mi historia personal: Aprendí principios de OO de construcción de objetos e Inversión de control mucho antes de aprender a usar contenedores de Inversión de Control como los contenedores de Unity o Castle Windsor. Adquirí el hábito de escribir código como este:

public class Foo 
{ 
    IService _service; 
    int _accountNumber; 

    public Foo(IService service, int accountNumber) 
    { 
     _service = service; 
     _accountNumber = accountNumber; 
    } 
    public void SaveAccount() 
    { 
     _service.Save(_accountNumber); 

    } 
} 
public class Program 
{ 
    public static void Main() 
    { 
     Foo foo = new Foo(new Service(),1234); 
     foo.Save(); 
    } 
} 

En este diseño, mi clase Foo es responsable de las cuentas de ahorro a la base de datos. Necesita un número de cuenta para hacer eso y un servicio para hacer el trabajo sucio. Esto es algo similar a las clases concreted que proporcionó anteriormente, donde cada objeto toma algunos valores únicos en el constructor. Esto funciona bien cuando instancia los objetos con su propio código. Puede pasar los valores apropiados en el momento correcto.

Sin embargo, cuando supe sobre los contenedores automáticos de inyección de dependencia, descubrí que ya no estaba instanciando manualmente Foo. El contenedor crearía una instancia de los argumentos del constructor para mí. Esto fue una gran conveniencia para los servicios como IService. Pero obviamente no funciona tan bien para enteros y cadenas y cosas por el estilo. En esos casos, proporcionaría un valor predeterminado (como cero para un entero).En cambio, me había acostumbrado a pasar en los valores específicos del contexto como el número de cuenta, nombre, etc ... así que tuve que ajustar mi estilo de codificación y diseño que ser así:

public class Foo 
{ 
    IService _service; 
    public Foo(IService service) 
    { 
     _service = service; 
    } 
    public void SaveAccount(int accountNumber) 
    { 
     _service.Save(accountNumber); 

    } 
} 
public class Program 
{ 
    public static void Main() 
    { 
     Foo foo = new Foo(new Service()); 
     foo.Save(1234); 
    } 
} 

Parece que tanto Las clases de Foo son diseños válidos. Pero el segundo es utilizable con inyección de dependencia automática, y el primero no.

+4

El segundo método Main() no debería ser más parecido a: Foo foo = new Foo (new Service()); foo.Save (1234); ? – McBainUK

+0

De hecho, debería ser foo.SaveAccount (1234) :) – sethidev

+0

No es interesante acerca de la inicialización 'int' allí. Acabo de tener un caso muy similar (dominio completamente diferente) donde necesitaba inicializar el tiempo de espera, resumí eso en un método, – Thomas

Cuestiones relacionadas