2010-03-11 5 views
30

Récemment, je suis passé à la libération Ninject 2.0 et a commencé à obtenir l'erreur suivante:erreur « Plus d'un liaisons de correspondance sont disponibles » lors de l'utilisation Ninject.Web.Mvc 2.0 et ASP.NET MVC 1.0

 
Error occured: Error activating SomeController 
More than one matching bindings are available. 
Activation path: 
    1) Request for SomeController 

Suggestions: 
    1) Ensure that you have defined a binding for SomeController only once. 

Cependant, je suis incapable de trouver un certain chemin de reproduction. Parfois, cela arrive, parfois non. J'utilise NinjectHttpApplication pour l'injection automatique des contrôleurs. Les contrôleurs sont définis dans un assemblage séparé:

public class App : NinjectHttpApplication 
{ 
    protected override IKernel CreateKernel() 
    { 
     INinjectModule[] modules = new INinjectModule[] { 
      new MiscModule(), 
      new ProvidersModule(), 
      new RepositoryModule(), 
      new ServiceModule() 
     }; 

     return new StandardKernel(modules); 
    } 

    protected override void OnApplicationStarted() 
    { 
     RegisterRoutes(RouteTable.Routes); 
     RegisterAllControllersIn("Sample.Mvc"); 
     base.OnApplicationStarted(); 
    } 

    /* ............. */ 

} 

Peut-être que quelqu'un connaît cette erreur.

Un conseil?

+0

FYI, ce n'est pas exclusif à ASP.NET MVC 1.0. Je l'ai juste fait se produire dans ASP.NET MVC 2.0 aussi bien. – mckamey

+2

J'ai créé un projet de reprographie pour cette situation et l'ai téléchargé dans le groupe Ninject-dev. J'espère que quelqu'un reconnaîtra le problème. Je n'étais pas capable de voir une solution facile. http://groups.google.com/group/ninject-dev/files – mckamey

+0

Pour info, j'ai vérifié que cela est corrigé pour moi dans la version 2.1.0.0 de NinjectNinject.Web.Mvc (MVC2). Dans la dernière version, vous n'avez plus besoin d'appeler RegisterAllControllersIn (...). – mckamey

Répondre

23

J'ai finalement trouvé ce problème récemment. Apparemment, la fonction NinjectHttpApplication.RegisterAllControllersIn() ne fait pas toutes les liaisons nécessaires. Il lie vos implémentations de contrôleur concret aux demandes IController. Par exemple, si vous avez une classe de contrôleur appelée SampleMvcController, qui hérite de System.Web.Mvc.Controller. Il ferait ce qui suit nommé la liaison au démarrage de l'application:

kernel.Bind<IController>().To(SampleMvcController).InTransientScope().Named("SampleMvc"); 

Mais lors du débogage du NinjectControllerFactory, je trouve cette demande sont en cours pour le noyau Ninject pour retourner un objet pour la classe « SampleMvcController », et non pour un béton implémentation de IController, en utilisant la liaison nommée de "SampleMvc". Pour cette raison, lorsque la première requête Web impliquant le SampleMvcController est effectuée, il crée une liaison de SampleMvcController à lui-même. Ce n'est pas thread sûr cependant. Donc, si vous avez plusieurs requêtes Web en même temps, les liaisons peuvent potentiellement se produire plus d'une fois, et maintenant vous avez cette erreur pour avoir plusieurs liaisons pour le SampleMvcController.

Vous pouvez vérifier cela en actualisant rapidement une URL MVC, juste après le redémarrage de votre application Web.

Le correctif:

La façon la plus simple de résoudre ce problème est de créer une nouvelle NinjectModule pour vos liaisons de contrôleur, et de charger ce module au démarrage de l'application. Au sein de ce module, vous auto lier chacun de vos contrôleurs définis, comme ceci:

class ControllerModule : StandardModule { 
     public override Load() { 
     Bind<SampleMvcController>().ToSelf(); 
     Bind<AnotherMvcController>().ToSelf(); 
     } 
    } 

Mais si cela ne vous dérange pas de modifier le code source Ninject, vous pouvez modifier la fonction RegisterAllControllersIn() à l'auto se lier chaque contrôleur, il rencontrer.

+1

Merci pour l'indice. Je vais essayer et marquer votre réponse. Avez-vous déjà informé Nate Kohari? =) –

+0

Merci pour cela, nous tirions nos cheveux avec cette question. – DavidWhitney

1

Etes-vous sûr que vous créez vraiment un nouveau Kernel entièrement nouveau à partir de zéro dans votre OnApplicationStarted chaque fois qu'il est appelé? Si ce n'est pas le cas, vous le créez une fois, mais vous risquez d'exécuter deux fois le bit d'enregistrement. Rappelez-vous que vous n'êtes pas garanti d'avoir une seule classe App instanciée jamais dans un AppDomain donné.

+0

Jetez un oeil à README dans le bas de la page http://github.com/enkari/ninject.web.mvc (Je pense que mon code est assez similaire). Cependant, quand je regarde à la méthode de NinjectHttpApplication Application_Start() http://github.com/enkari/ninject.web.mvc/blob/master/mvc1/src/Ninject.Web.Mvc/NinjectHttpApplication.cs cela me fait penser que la configuration est fait par instance de noyau unique. –

+0

@Cray: Je vais regarder à temps - désolé de ne pas avoir le temps maintenant, mais je voulais répondre au cas où il vous débloquait. Le point principal de mon article est de dire "cette exception suggère que vous mettiez un ensemble dupliqué ou des liaisons dans le noyau, peut-être en l'initialisant deux fois dans le même noyau", peu importe où vous avez reçu le code. un reflet précis de votre code réel qui échoue. (J'ai fait zéro avec le RT2 NI2 mais beaucoup avec diverses versions pre-2) –

0

j'ai ajouté à mon fichier global.ascx.cs:

 public void RegisterAllControllersInFix(Assembly assembly) 
    { 
     RegisterAllControllersInFix(assembly, GetControllerName); 
    } 

    public void RegisterAllControllersInFix(Assembly assembly, Func<Type, string> namingConvention) 
    { 
     foreach (Type type in assembly.GetExportedTypes().Where(IsController)) 
      Kernel.Bind(type).ToSelf(); 
    } 

    private static bool IsController(Type type) 
    { 
     return typeof(IController).IsAssignableFrom(type) && type.IsPublic && !type.IsAbstract && !type.IsInterface; 
    } 

    private static string GetControllerName(Type type) 
    { 
     string name = type.Name.ToLowerInvariant(); 

     if (name.EndsWith("controller")) 
      name = name.Substring(0, name.IndexOf("controller")); 

     return name; 
    } 

Puis ont appelé de ma méthode OnApplicationStarted() comme suit:

 RegisterAllControllersIn(Assembly.GetExecutingAssembly()); 
     RegisterAllControllersInFix(Assembly.GetExecutingAssembly()); 

Difficile de savoir si ce qu'il fixe bien parce que c'est si intermittent.

+0

Essayez quelque chose comme WebClient.DownloadString ("http: // yoursite /") en boucle, ... dans plusieurs threads =) –

+0

Will do - merci Denis – coalvilledave

16

J'ai eu affaire à ce problème pendant des mois. J'ai essayé tant d'options mais j'étais incapable de trouver une solution. Je savais que c'était un problème de threading parce que cela ne se produirait que quand il y avait une charge lourde sur mon site. Tout récemment, un bug a été signalé et corrigé dans le code source ninject qui résout ce problème.

Voici une référence au issue. Il a été corrigé dans la version 2.1.0.70 de la source Ninject. Le principal changement était KernelBase.cs en supprimant la ligne

context.Plan = planner.GetPlan(service); 

et le remplacer par

lock (planner) 
{ 
    context.Plan = planner.GetPlan(service); 
} 

Pour utiliser cette nouvelle version avec MVC, vous aurez besoin d'obtenir la dernière version de Ninject puis obtenir la dernière version de ninject.web.mvc. Construisez ninject.web.mvc avec la nouvelle version de Ninject.

J'ai utilisé cette nouvelle construction pendant environ une semaine avec une lourde charge et aucun problème. C'est la période la plus longue sans problème et je considère que c'est une solution.

+0

Génial! C'est une grande nouvelle. Je me suis occupé de ça moi-même. Merci d'avoir fourni cette réponse, Steve! –

1

Ma réponse était un peu plus évidente.

J'avais déclaré la liaison pour l'un de mes contrôleurs plus d'une fois lors du refactor de mon code.