2010-06-30 17 views
9

J'ai un problème qui semble très similaire à celui décrit dans http://markmail.org/message/6rlrzkgyx3pspmnf qui est à propos de singleton créant plus d'une instance si vous y accédez en utilisant différents types de services.Liaison singleton à plusieurs services dans Ninject

J'utilise la dernière version de Ninject 2 pour Compact Framework et la question exacte que je vais avoir est que si je lie la même méthode de fournisseur à:

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

Il semble créer 2 cas de Service si je résous à la fois comme IService et Service.

Cela provoque une exception de dépendance circulaire lors de la résolution du service.

Est-ce intentionnellement ou est-ce un bug?

+0

BTW Je crois qu'il y a des incohérences à nettoyer dans les versions 2.3 et 2.4 de Ninject en faisant en sorte que les choses que vous réutilisez de cette manière ne soient activées et/ou nettoyées qu'une fois –

+0

Voir la réponse spécifique à V3: http:// stackoverflow.com/questions/10206049/ninject-est-ce-possible-à-bind-différent-interfaces-à-la-même-instance-de-ac –

+0

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

Répondre

11

En V3, il existe enfin une solution pour cela sous la forme new overloads on Bind, voir this related: question.


Si vous voulez que le singleton à partager, vous devez changer votre deuxième Bind à:

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

Re références circulaires et de la confusion, etc. En interne l'implicite autolimitation ajoutera une liaison implicite inscription pour le service. Vous devriez publier l'exception.

EDIT: Re votre commentaire. Si vous le faites comme ceci:

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

alors aucune classe auto implicite La liaison est généré lorsque IService se résoudre - il utilise l'existant. - Cet exemple aurait l'ordre correct mais celui ci-dessus est logique en fonction de ma lecture de la source et de la façon dont elle génère les auto-liaisons de classe implicites.

+0

Cela provoque en fait un débordement de pile dans mon scénario. Quand j'aurai le temps, j'essaierai d'isoler le problème et de poster un exemple minimaliste de mon scénario. –

+0

@Michal: Réponse éditée en –

+0

Heh, semble que j'ai appliqué votre première suggestion faux - J'ai utilisé Get au lieu de Get . D'oh. ;) –

3

Nous avons utilisé la méthode de Ruben dans notre projet, mais avons trouvé que ce n'était pas intuitif de savoir pourquoi vous reveniez au noyau dans votre liaison. J'ai créé une méthode d'extension et de classe aide (ci-dessous) afin que vous pouvez faire ceci:

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

qui semblait exprimer l'intention plus clairement à moi.

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

Bel exemple. BTW Je crois qu'il y a un nouveau 'Bind <>. ToBinding' ou quelque chose de ce genre dans, ou dans une extension/dans un blog @Remo Gloor sur le chemin qui produit un mécanisme similaire au vôtre. –

+0

@RubenBartelink est correct: https://github.com/ninject/ninject.extensions.contextpreservation –

+0

Lien pour le poste auquel je faisais allusion dans un commentaire précédent http://www.planetgeek.ch/2011/12/30/new- features-and-changes-of-ninject-3-0/ –

5

Par ailleurs, Ninject 3 allows this syntax:

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

Ou, similaire:

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

Cette dernière approche fonctionne mieux si vous avez de nombreux services, ou si vous découvriez les services dynamiquement au moment de l'exécution (vous pouvez passer directement les arguments params en tant que tableau).

+2

C'est la voie à suivre, mais n'était probablement pas disponible lorsque la question a été posée à l'origine. – BatteryBackupUnit

+0

@BatteryBackupUnit: concernant votre édition: cela ne me ressemble pas à une syntaxe C# valide. Pouvez-vous le clarifier, ou peut-être l'ajouter comme votre propre réponse? – StriplingWarrior

+1

désolé, était un peu trop enthousiaste je suppose. pour Bind, il existe une surcharge 'Bind (params Type [] services)' qui accepte (presque) n'importe quel nombre de types. Le 'Bind ' maximum à 4 types. L'autre surcharge est donc bénéfique si vous avez plus de 4 types ou si vous utilisez la réflexion pour obtenir les types. Pourriez-vous ajouter ceci à votre propre réponse avec une meilleure «formulation»? Cela rendrait la réponse complète à mon humble avis. – BatteryBackupUnit