2010-06-30 12 views
9

Tengo un problema que parece muy similar al descrito en http://markmail.org/message/6rlrzkgyx3pspmnf, que trata de que el singleton realmente cree más de una instancia si está accediendo a él utilizando diferentes tipos de servicio.Vinculando singleton a servicios múltiples en Ninject

estoy usando la última versión de Ninject 2 para Compact Framework y el problema exacto que estoy teniendo es que si Ato el mismo método que el proveedor:

Func<Service> serviceCreator =() => new Service(false); 
kernel.Bind<IService>().ToMethod(serviceCreator).InSingletonScope(); 
kernel.Bind<Service>().ToMethod(serviceCreator).InSingletonScope(); 

Parece ser la creación de 2 casos de Servicio si resuelvo ambos como servicio de IServicio y Servicio.

Esto provoca una excepción de dependencia circular al resolver el servicio.

¿Es esto por diseño, o es un error?

+0

BTW Creo que hay algunas inconsistencias que se limpian en las versiones 2.3 y 2.4 de Ninject y asegúrese de que las cosas que reutiliza de esta manera solo se activen y/o limpien una vez –

+0

Consulte la respuesta específica de V3: http://desbordamiento de pila.com/questions/10206049/ninject-is-it-possible-to-bind-different-interfaces-to-the-same-instance-of-ac –

+0

relacionado: http://stackoverflow.com/questions/8303661/ninject- binding-interface-to-interface/8303826 # comment16639462_8303826 –

Respuesta

11

En V3, finalmente hay una solución para esto en la forma de new overloads on Bind, vea this related: question.


Si desea que el producto único a ser compartida, tiene que cambiar su segundo Bind a:

kernel.Bind<Service>().ToMethod(()=>kernel.Get<IService>()).InSingletonScope(); 

Re referencias circulares y confusión etc. Internamente, la implícita autovinculantes agregará una unión implícita registro para el Servicio. Deberías publicar la excepción.

EDIT: Re su comentario. Si lo haces de esta manera:

Func<Service> serviceCreator =() => new Service(false); 
kernel.Bind<Service>().ToMethod(serviceCreator).InSingletonScope(); 
kernel.Bind<IService>().ToMethod(()=>kernel.Get<Service>()).InSingletonScope(); 

Entonces hay implícita Clase unión libre se genera cuando se resuelva IService - que utiliza la ya existente.

There was another Q here on SO in recent weeks someone was doing this type of thing but was running into an issue with IInitializable - ese ejemplo tendría el orden correcto, pero el anterior tiene sentido en función de mi lectura de la fuente y la forma en que genera las auto-vinculaciones de clases implícitas.

+0

Esto realmente causa un desbordamiento de pila en mi escenario. Cuando tenga tiempo, intentaré aislar el problema y publicar un ejemplo minimalista de mi escenario. –

+0

@Michal: Respuesta editada en –

+0

Heh, parece que he aplicado su primera sugerencia mal - He usado Obtener en lugar de Obtener . D'oh. ;) –

3

Utilizamos el método de Ruben en nuestro proyecto, pero descubrimos que no era intuitivo por qué volvíamos al Kernel en su encuadernación. He creado una clase de método de extensión y ayudante (abajo) para que pueda hacer esto:

kernel.Bind<IService>().ToExisting().Singleton<Service>(); 

que parecía expresar la intención más clara para mí.

public static class DIExtensions 
{ 
    public static ToExistingSingletonSyntax<T> ToExisting<T>(this IBindingToSyntax<T> binding) 
    { 
     return new ToExistingSingletonSyntax<T>(binding); 
    } 
} 

// Had to create this intermediate class because we have two type parameters -- the interface and the implementation, 
// but we want the compiler to infer the interface type and we supply the implementation type. C# can't do that. 
public class ToExistingSingletonSyntax<T> 
{ 
    internal ToExistingSingletonSyntax(IBindingToSyntax<T> binding) 
    { 
     _binding = binding; 
    } 

    public IBindingNamedWithOrOnSyntax<T> Singleton<TImplementation>() where TImplementation : T 
    { 
     return _binding.ToMethod(ctx => ctx.Kernel.Get<TImplementation>()).InSingletonScope(); 
    } 


    private IBindingToSyntax<T> _binding; 
} 
+0

Buen ejemplo. Por cierto, creo que hay un nuevo 'Bind <>. ToBinding' o algo por el estilo, o en una extensión/en una publicación de blog de @Remo Gloor en la forma en que se produce un mecanismo similar al tuyo. –

+0

@RubenBartelink es correcto: https://github.com/ninject/ninject.extensions.contextpreservation –

+0

Enlace para la publicación a la que aludía en un comentario anterior http://www.planetgeek.ch/2011/12/30/new- features-and-changes-of-ninject-3-0/ –

5

Por cierto, Ninject 3 allows this syntax:

kernel.Bind<IService, Service>().ToMethod(serviceCreator).InSingletonScope(); 

O, de manera similar:

kernel.Bind(typeof(IService), typeof(Service)).ToMethod(serviceCreator).InSingletonScope(); 

Este último enfoque funciona mejor si tiene muchos servicios, o si usted descubrió los servicios dinámicamente en tiempo de ejecución (puede pasar los argumentos de estilo params como una matriz directamente).

+2

Este es el camino a seguir, pero probablemente no estuvo disponible cuando la pregunta fue hecha originalmente. – BatteryBackupUnit

+0

@BatteryBackupUnit: con respecto a su edición: eso no se parece a la sintaxis C# válida para mí. ¿Puedes aclararlo o posiblemente agregarlo como tu propia respuesta? – StriplingWarrior

+1

lo siento, estaba un poco ansioso, supongo. para Bind, hay una sobrecarga 'Bind (params Type [] services)' que acepta (casi) cualquier cantidad de tipos. El 'Bind ' alcanza un máximo de 4 tipos. Por lo tanto, la otra sobrecarga es beneficiosa si tienes más de 4 tipos o si usas el reflejo para obtener los tipos. ¿Podría agregar esto a su propia respuesta con una mejor "redacción"? Haría la respuesta completa en mi humilde opinión. – BatteryBackupUnit

Cuestiones relacionadas