2010-03-11 4 views
30

Recentemente ho passato a Ninject 2,0 rilascio e cominciato ottenere il seguente errore:errore "Più di un binding di corrispondenza sono disponibili" quando si usa Ninject.Web.Mvc 2.0 e 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. 

Tuttavia, non riesco a trovare un determinato percorso di riproduzione. A volte capita, a volte no. Sto usando NinjectHttpApplication per l'iniezione automatica dei controller. I controller sono definiti in un assembly separato:

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(); 
    } 

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

} 

Forse qualcuno ha familiarità con questo errore.

Qualche consiglio?

+0

FYI, non è esclusivo di ASP.NET MVC 1.0. Ho appena fatto accadere anche in ASP.NET MVC 2.0. – mckamey

+2

Ho creato un progetto di riproduzione per questa situazione e l'ho caricato nel gruppo Ninject-dev. Spero che qualcuno riconoscerà il problema. Non ero in grado di vedere una soluzione facile. http://groups.google.com/group/ninject-dev/files – mckamey

+0

FYI, ho verificato che questo è stato risolto per me nella versione 2.1.0.0 di NinjectNinject.Web.Mvc (MVC2). Nell'ultima versione non è più necessario chiamare RegisterAllControllersIn (...). – mckamey

risposta

23

Alla fine ho finalmente risolto questo problema. Apparentemente, la funzione NinjectHttpApplication.RegisterAllControllersIn() non esegue tutti i binding corretti necessari. Lega le implementazioni del controller concreto alle richieste di IController. Ad esempio, se si dispone di una classe controller chiamata SampleMvcController, che eredita da System.Web.Mvc.Controller. Sarebbe fare quanto segue chiamato binding durante l'avvio dell'applicazione:

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

Ma quando il debug del NinjectControllerFactory, trovo che la richiesta sono stati fatti per il Ninject Kernel per restituire un oggetto per la classe "SampleMvcController", non per un concreto implementazione di IController, utilizzando l'associazione denominata di "SampleMvc".

Per questo motivo, quando viene eseguita la prima richiesta Web che coinvolge SampleMvcController, crea un binding di SampleMvcController su se stesso. Questo non è thread-sicuro però. Pertanto, se si eseguono più richieste Web contemporaneamente, i binding possono potenzialmente verificarsi più di una volta e ora si ha questo errore per avere più binding per SampleMvcController.

È possibile verificare ciò aggiornando rapidamente un URL MVC, subito dopo il riavvio dell'applicazione Web.

La correzione:

Il modo più semplice per risolvere questo problema è quello di creare un nuovo NinjectModule per le associazioni del controller, e per caricare questo modulo durante l'avvio dell'applicazione. All'interno di questo modulo, si auto associare ogni controller definiti, in questo modo:

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

Ma se non ti dispiace cambiare il codice sorgente Ninject, è possibile modificare la funzione RegisterAllControllersIn() per sé legare ogni controller è si imbatte.

+1

Grazie per il suggerimento. Ci proverò e segnerò la tua risposta. Hai già avvertito Nate Kohari? =) –

+0

Grazie per questo, stavamo tirando i capelli con questo problema. – DavidWhitney

1

Sei sicuro di creare davvero un singolocompletamente nuovo da zero nel tuo OnApplicationStarted ogni volta che viene richiamato? Se non lo sei e in realtà lo stai creando una volta ma potenzialmente eseguendo il bit di registrazione due volte. Ricorda che non ti è garantito che tu abbia sempre una classe App istanziata sempre all'interno di un determinato AppDomain.

+0

Dai un'occhiata a README nella parte inferiore della pagina http://github.com/enkari/ninject.web.mvc (penso che il mio codice sia piuttosto simile). Tuttavia, quando guardo al metodo NinjectHttpApplication Application_Start() http://github.com/enkari/ninject.web.mvc/blob/master/mvc1/src/Ninject.Web.Mvc/NinjectHttpApplication.cs mi viene da pensare che la configurazione viene eseguito per singola istanza del kernel. –

+0

@Cray: cercherò in tempo - mi spiace non avere tempo ora ma volevo rispondere in caso di sblocco. Il punto principale del mio post è quello di dire "questa eccezione suggerisce che hai messo un set duplicato o binding nel kernel in qualche modo, magari inizializzando due volte nello stesso kernel", indipendentemente da dove hai ottenuto il codice e indipendentemente dal fatto che la tua domanda rappresenti una riflessione accurata del tuo codice reale che sta fallendo. (Ho fatto zero con RTM NI2, ma ho molte versioni pre-2) –

0

ho aggiunto questo al mio file 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; 
    } 

poi chiamato dal mio metodo OnApplicationStarted() come segue:

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

difficile sapere se questo riparato, però perché è così intermittente.

+0

Provare qualcosa di simile WebClient.DownloadString ("http: // yoursite /") in loop, ... in diversi thread =) –

+0

Farà - grazie Denis – coalvilledave

16

Ho avuto a che fare con questo problema da mesi. Ho provato così tante opzioni ma non sono riuscito a trovare una soluzione. Sapevo che si trattava di un problema di threading perché si verificava solo quando c'era un carico pesante sul mio sito. Recentemente è stato segnalato un bug e risolto nel codice sorgente ninject che risolve questo problema.

Ecco un riferimento al issue. È stato risolto nella build 2.1.0.70 della sorgente Ninject. Il cambiamento chiave era in KernelBase.cs rimuovendo la linea

context.Plan = planner.GetPlan(service); 

e la sua sostituzione con

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

Per utilizzare questa nuova build con MVC sarà necessario per ottenere l'ultima build di Ninject quindi ottenere la versione più recente di ninject.web.mvc. Costruisci ninject.web.mvc con la nuova build Ninject.

Ho utilizzato questa nuova build per circa una settimana con un carico pesante e senza problemi. È il più lungo che è passato senza problemi, quindi considererei questa una soluzione.

+0

Impressionante! Questa è una grande novità. Ho avuto a che fare con me stesso. Grazie per aver fornito questa risposta, Steve! –

1

La mia risposta è stata un po 'più ovvia.

mi aveva dichiarato l'associazione per uno dei miei regolatori di più di una volta durante il refactoring del mio codice.

Problemi correlati