mvc injection dependency ninject

injection - Evita que Ninject realice llamadas Inicialice varias veces al enlazar a varias interfaces



ninject github (3)

Proyecto 3

Ninject 3.0 ahora admite múltiples tipos genéricos en la llamada a vincular, lo que está intentando hacer se puede lograr fácilmente en una sola declaración encadenada.

kernel.Bind<IService1, IService2>() .To<ConcreteService>() .InSingletonScope();

Ninject 2

Está configurando dos enlaces diferentes K => T y L => T. Solicitar instancias de L devolverá instancias transitorias de T. Solicitar K devolverá una instancia única de T.

En Ninject 2.0, un ámbito de objetos es por interfaz de servicio vinculado a una devolución de llamada de ámbito.

Cuando tengas

Bind<IFoo>...InSingletonScope(); Bind<IBar>...InSingletonScope();

Estás creando dos ámbitos diferentes.

Estás diciendo que "El enlace con IFoo se resolverá con el mismo objeto que se devolvió cuando se llamó a .Get". y "El enlace con IBar se resolverá con el mismo objeto que se devolvió cuando se llamó a .Get."

puede encadenar los enlaces, pero deberá eliminar IInializable, ya que causará una inicialización duplicada cuando se active la instancia:

kernel.Bind<IBoo>() .To<Foo>() .InSingletonScope(); .OnActivation(instance=>instance.Initialize()); kernel.Bind<IBaz>() .ToMethod( ctx => (IBaz) ctx.Kernel.Get<IBoo>() );

o

kernel.Bind<Foo>().ToSelf().InSingletonScope() .OnActivation(instance=>instance.Initialize()); kernel.Bind<IBaz>().ToMethod( ctx => ctx.Kernel.Get<Foo>() ); kernel.Bind<IBoo>().ToMethod( ctx => ctx.Kernel.Get<Foo>() );

con el fin de obtener múltiples interfaces para resolver a la misma instancia de singleton. Cuando veo situaciones como esta, siempre tengo que preguntar si tu objeto está haciendo demasiado si tienes un singleton con dos responsabilidades.

Tenemos un servicio de singleton concreto que implementa Ninject.IInitializable y 2 interfaces. El problema es que los servicios Initialize-methdod se llaman 2 veces, cuando solo se desea uno. Estamos utilizando .NET 3.5 y Ninject 2.0.0.0.

¿Hay algún patrón en Ninject que evite que esto suceda? Ninguna de las interfaces implementa Ninject.IInitializable . la clase de servicio es:

public class ConcreteService : IService1, IService2, Ninject.IInitializable { public void Initialize() { // This is called twice! } }

Y el módulo se ve así:

public class ServiceModule : NinjectModule { public override void Load() { this.Singleton<Iservice1, Iservice2, ConcreteService>(); } }

donde Singleton es un método de extensión definido de esta manera:

public static void Singleton<K, T>(this NinjectModule module) where T : K { module.Bind<K>().To<T>().InSingletonScope(); } public static void Singleton<K, L, T>(this NinjectModule module) where T : K, L { Singleton<K, T>(module); module.Bind<L>().ToMethod(n => n.Kernel.Get<T>()); }

Por supuesto, podríamos agregar bool initialized-member a ConcreteService e inicializar solo cuando sea falso, pero parece un poco pirateado. Y requeriría repetir la misma lógica en cada servicio que implementa dos o más interfaces.

¡Gracias por todas las respuestas! ¡Aprendí algo de todos ellos! (Me está costando mucho decidir cuál marca es correcta).

Terminamos creando una interfaz IAactivable y extendiendo el kernel de ninject (también eliminó muy bien las dependencias de nivel de código a ninject, aunque todos los atributos aún permanecen).


Actualización: Bastante seguro de que el uso de las múltiples sobrecargas de enlace de V3 solucionará esto; Ver este Q / A

Buena pregunta.

De mirar la fuente, el bit de inicialización ocurre después de cada Activate . Tu Bind...ToMethod cuenta como uno también. La estrategia se aplica de manera bastante uniforme, no hay forma de optar por no participar en casos particulares.

Sus opciones de solución son utilizar una OnActivation explícita en su Bind que lo hará condicionalmente (pero hacerlo de una manera general requeriría mantener un conjunto de objetos inicializados (no se ha buscado para ver si existe un mecanismo para esconder una bandera contra un objeto activado)), o para hacer que su valor inicialice el idempotente por cualquier medio que sea más limpio para usted.

EDITAR:

internal interface IService1 { } internal interface IService2 { } public class ConcreteService : IService1, IService2, Ninject.IInitializable { public int CallCount { get; private set; } public void Initialize() { ++CallCount; } } public class ServiceModule : NinjectModule { public override void Load() { this.Singleton<IService1, IService2, ConcreteService>(); } }

Teniendo en cuenta los siguientes ayudantes:

static class Helpers { public static void Singleton<K, T>( this NinjectModule module ) where T : K { module.Bind<K>().To<T>().InSingletonScope(); } public static void Singleton<K, L, T>( this NinjectModule module ) where T : K, L { Singleton<T, T>( module ); module.Bind<K>().ToMethod( n => n.Kernel.Get<T>() ); module.Bind<L>().ToMethod( n => n.Kernel.Get<T>() ); } }

@Ian Davis et al. El problema es ese:

class Problem { [Fact] static void x() { var kernel = new StandardKernel( new ServiceModule() ); var v1 = kernel.Get<IService1>(); var v2 = kernel.Get<IService2>(); var service = kernel.Get<ConcreteService>(); Console.WriteLine( service.CallCount ); // 3 Assert.AreEqual( 1, service.CallCount ); // FAILS } }

Porque cada activación (por Bind ) se inicializa cada vez.

EDIT 2: igual cuando se utiliza la siguiente versión un poco más simplificada:

static class Helpers { public static void Singleton<K, L, T>( this NinjectModule module ) where T : K, L { module.Bind<T>().ToSelf().InSingletonScope(); module.Bind<K>().ToMethod( n => n.Kernel.Get<T>() ); module.Bind<L>().ToMethod( n => n.Kernel.Get<T>() ); } }


Creo que una de las opciones es crear el objeto en el módulo y vincular su objeto a cada una de las interfaces.

Por cierto, trate de no usar ningún código específico de contenedor en su código de producción. Si tiene que hacer eso, use algún ayudante y aíslelos en el proyecto del módulo.

public class ServiceModule : NinjectModule { public override void Load() { ConcreteService svc = new ConcreteService(); Bind<IService1>().ToConstant(svc); Bind<IService2>().ToConstant(svc); .... } }