tipos - patron de inyeccion de dependencias c#
Crear una instancia usando Ninject con parámetros adicionales en el constructor (1)
Decidí comenzar a usar Ninject y enfrentar un problema. Digamos que tengo el siguiente escenario. Tengo una interfaz IService
y 2 clases que implementan esta interfaz. Y también tengo una clase, que tiene un constructor que obtiene IService y un int . ¿Cómo puedo crear una instancia de esta clase con Ninject (no quiero conectar esta int, quiero pasarla cada vez que obtengo una instancia)?
Aquí hay un código que ilustra la situación:
interface IService
{
void Func();
}
class StandardService : IService
{
public void Func()
{
Console.WriteLine("Standard");
}
}
class AlternativeService : IService
{
public void Func()
{
Console.WriteLine("Alternative");
}
}
class MyClass
{
public MyClass(IService service, int i)
{
this.service = service;
}
public void Func()
{
service.Func();
}
IService service = null;
}
class Program
{
static void Main(string[] args)
{
IKernel kernel = new StandardKernel(new InlineModule(
x => x.Bind<IService>().To<AlternativeService>(),
x => x.Bind<MyClass>().ToSelf()));
IService service = kernel.Get<IService>();
MyClass m = kernel.Get<MyClass>();
m.Func();
}
}
El With.ConstructorArgument
existía en 1.0 para este propósito. En 2.0, la sintaxis ha cambiado ligeramente: - With.Parameters.ConstructorArgument con ninject 2.0
Consulte Inyectar valor en la dependencia inyectada para obtener más detalles y ejemplos de cómo usar el contexto, los proveedores y los argumentos para pasar cosas como esta de forma más correcta.
EDITAR: Como Steven ha elegido fingir que mi comentario es irrelevante, será mejor que aclare lo que estoy diciendo con algunos ejemplos (para 2.0):
MyClass m = kernel.Get<MyClass>( new ConstructorArgument( "i", 2) );
lo cual para mí es muy claro y establece exactamente lo que está sucediendo.
Si se encuentra en una posición donde puede determinar el parámetro de una manera más global, puede registrar un proveedor y hacerlo así:
class MyClassProvider : SimpleProvider<MyClass>
{
protected override MyClass CreateInstance( IContext context )
{
return new MyClass( context.Kernel.Get<IService>(), CalculateINow() );
}
}
Y registrarlo así:
x => x.Bind<MyClass>().ToProvider( new MyClassProvider() )
NB, el bit CalculateINow()
es donde pondrías tu lógica como en la primera respuesta.
O hazlo más complejo así:
class MyClassProviderCustom : SimpleProvider<MyClass>
{
readonly Func<int> _calculateINow;
public MyClassProviderCustom( Func<int> calculateINow )
{
_calculateINow = calculateINow;
}
protected override MyClass CreateInstance( IContext context )
{
return new MyClass( context.Kernel.Get<IService>(), _calculateINow() );
}
}
Que te registrarías así:
x => x.Bind<MyClass>().ToProvider( new MyClassProviderCustom( ( ) => new Random( ).Next( 9 ) ) )
ACTUALIZACIÓN: Los mecanismos más nuevos que exhiben patrones muy mejorados con menos repetición que los anteriores están incorporados en la extensión Ninject.Extensions.Factory
, consulte: https://github.com/ninject/ninject.extensions.factory/wiki
Como se indicó anteriormente, si necesita pasar un parámetro diferente cada vez y tiene múltiples niveles en el gráfico de dependencia, es posible que deba hacer algo como esto .
Una última consideración es que, debido a que no ha especificado Using<Behavior>
, va a establecerse de forma predeterminada como predeterminada / predeterminada en las opciones para el kernel ( TransientBehavior
en la muestra), lo que podría representar un hecho que la fábrica calcula i
en el móvil de la mosca [por ejemplo, si el objeto se estaba almacenando en caché]
Ahora, para aclarar algunos otros puntos en los comentarios que están siendo FUDed y pasados por alto. Algunas cosas importantes a considerar sobre el uso de DI, ya sea Ninject o lo que sea, es:
Haga todo lo posible por la inyección del constructor, de modo que no necesite utilizar atributos y trucos específicos del contenedor. Hay una buena publicación en el blog llamada Your IoC Container is Showing .
Minimice el código que va al contenedor y solicite cosas; de lo contrario, su código se combinará con a) el contenedor específico (que el CSL puede minimizar) b) la forma en que se distribuye todo el proyecto. Hay buenas publicaciones de blog que muestran que CSL no está haciendo lo que crees que hace. Este tema general se conoce como Ubicación de Servicio vs Inyección de Dependencia . ACTUALIZACIÓN: Consulte http://blog.ploeh.dk/2011/07/28/CompositionRoot.aspx para obtener una explicación detallada y completa.
Minimiza el uso de estáticas y singletons
No suponga que solo hay un contenedor [global] y que puede pedirlo siempre que lo necesite como una buena variable global. El uso correcto de varios módulos y
Bind.ToProvider()
le da una estructura para administrar esto. De esta forma, cada subsistema independiente puede funcionar por sí mismo y no tendrá componentes de bajo nivel vinculados a componentes de nivel superior, etc.
Si alguien quiere completar los enlaces a los blogs a los que me refiero, agradecería que (todos ya están vinculados desde otras publicaciones en SO, así que todo esto es solo una duplicación que he introducido con el objetivo de evitar la confusión de una respuesta engañosa.)
¡Ahora, si solo pudiera entrar Joel y realmente me aclarase qué sintaxis es buena y / o la forma correcta de hacerlo!
ACTUALIZACIÓN: Si bien esta respuesta es claramente útil a partir de la cantidad de votos por votos acumulados, me gustaría hacer las siguientes recomendaciones:
- Lo anterior se siente como un poco anticuado y para ser honesto refleja una gran cantidad de pensamiento incompleto que casi se siente embarazoso desde leer Inyección de Dependencia en .net - Correr y comprar ahora - no se trata solo de DI, la primera mitad es un tratamiento completo de todas las preocupaciones de arquitectura que lo rodean de un hombre que ha pasado demasiado tiempo aquí dando vueltas alrededor de la etiqueta de inyección de dependencia.
- Lee las publicaciones más votadas de Mark Seemann aquí en SO ahora mismo : aprenderás técnicas valiosas de cada uno