2013-02-15 17 views
12

Ho problemi a risolvere 404 risposte nel mio progetto Asp.Net MVC 4. È stato creato con il targeting VS2012 4.5.404 su controller in gruppi esterni

Ho viste e controller precompilati incorporati in DLL autonome. Sono in grado di caricare dinamicamente le DLL e ispezionarle dal mio progetto principale, invocando anche metodi su di esse; tuttavia, sembra che MVC Framework non sia a conoscenza dei controller. Sono qui vicino, ma manca qualcosa.

Sfondo sui controller e viste

controllori sono costruiti in un progetto MVC stand-alone e ereditare da Controller. Niente di troppo interessante in corso lì. Le viste utilizzano RazorGenerator e diventano classi che vivono nel progetto.

L'output del progetto è una DLL che contiene correttamente i controller e le viste.

Le DLL implementano un'interfaccia specifica, la chiameremo IPlugin, in una classe separata (non parte di un controller) nella libreria.

Caricamento le DLL

Esecuzione come admin in Visual Studio compilo la mia applicazione, che è ospitato in IIS. Con il progetto creato, inserisco una DLL plugin nella mia directory "Plugin". Senza debug (questo diventa importante in seguito), apro IE e navigo nel sito. Nota che a questo punto l'app è stata creata, ma non viene mai eseguita, quindi gli eventi di avvio verranno attivati. Tutto qui è ancora coerente se riciclo il pool di app.

Ho una classe Startup con due metodi, PreStart e PostStart e richiamare i metodi usando WebActivator.PreApplicationStartMethod e WebActivator.PostApplicationStartMethod rispettivamente.

PreStart è dove faccio la seguente:

  • ottenere un elenco di tutte le DLL plugin nella mia cartella "Plugins"
  • Copia tutti i plugin per AppDomain.CurrentDomain.DynamicDirectory
  • Caricare il tipo ... se contiene un IPlugin ho poi
    • aggiungere l'assembly al BuildManager
    • chiamata alcuni dei metodi della classe che imp ELEMENTI IPlugin

In 'poststart' Faccio questo pezzo di codice (basato sul codice da RazorGenerator.Mvc):

foreach (var assembly in Modules.Select(m=>m.Value)) 
{ 
    var engine = new PrecompiledMvcEngine(assembly) 
    { 
     UsePhysicalViewsIfNewer = HttpContext.Current.Request.IsLocal 
    }; 

    ViewEngines.Engines.Insert(0, engine); 
    VirtualPathFactoryManager.RegisterVirtualPathFactory(engine); 
} 

Modules in questo contesto è una coppia chiave/valore in cui il i valori sono gli assembly caricati. Lo scopo di questo codice è assicurarsi che MVC sia a conoscenza delle visualizzazioni aggiungendo un motore di visualizzazione per ogni assembly che sa come risolvere le viste (questo fa parte di RazorGenerator).

Come lo so sono vicino (ma chiaramente Mancando il sigaro)

IPlugin definisce un metodo chiamato RegisterRoutes dove, avete indovinato, percorsi devono essere registrati per coloro che implementa l'interfaccia. Io chiamo questo metodo in PreStart e le rotte sono aggiunte - Ho verificato che queste esistano nella mia tabella dei percorsi. Per esempio, su un percorso definito nel mio plug-in, creata attraverso l'invocazione dinamica del metodo durante la PreStart, vedo qualcosa di simile a questo come un DataToken in sede di esame miei percorsi:

Namespaces = Plugin.Name.Controllers 

Quindi, il percorso viene registrato, il l'assembly è stato caricato, ho verificato che la DLL sia correttamente copiata nella DynamicDirectory dell'AppDomain. Sono in grado di invocare membri di classi caricate dinamicamente in fase di runtime. Ma quando navigo verso l'URL che corrisponde alla route ottengo un 404. Questo è non un "impossibile trovare la vista" YSOD, è più simile a non trovare il controller affatto.

Ecco la parte che mi confonde da me: se, a questo punto, senza fare nulla, torno a Visual Studio e prendo F5 ... tutto funziona.

È come se Visual Studio stia diventando consapevole del controller in qualche modo che non riesco a identificare e MVC Framework si sta occupando di esso.

Infine, una domanda

Quello che mi manca, e come faccio ad avere il framework MVC di essere a conoscenza del mio controller?

E hey, a questo punto, se stai ancora leggendo questo, grazie. :)

+0

1. VS sta usando Cassini? Prova a cambiarlo in IIS Express e controlla se continua a funzionare correttamente. 2. Provare a installare [RouteDebugger] (http://nuget.org/packages/routedebugger) - potrebbe darti qualche indizio se i percorsi vengono registrati correttamente su IIS – Pranav

+0

Grazie a @Pranav, ma è già su IIS. Il debugger di route mostra che i percorsi funzionano. – MisterJames

+1

Potrebbe essere un problema? http://stackoverflow.com/questions/14971895/using-precompiledmvcengine-findview-throws-invalidoperationexception-and-looks-f – Tengiz

risposta

5

Si scopre che questo è un bug in Asp.Net stesso.

Dopo aver discusso il problema con Eilon Lipton of the Asp.Net team, e pensando che questo fosse qualcosa di sbagliato nel MVC Framework, Eilon e un paio di membri del team hanno scavato nelle cose e hanno scoperto che l'errore era a un livello inferiore per questa conversazione: http://aspnetwebstack.codeplex.com/discussions/403529

Hanno anche suggerito una soluzione alternativa che includesse un altro invitare BuildManager dopo la chiamata a AddReferencedAssembly, che ho implementato tramite il seguente codice:

// Add the plugin as a reference to the application 
    BuildManager.AddReferencedAssembly(assembly); 
    BuildManager.AddCompilationDependency(assembly.FullName); 

Ciò consente di aggiungere ulteriori controller/vista compilati in fase di avvio nel vostro pre-applicazione fase init. Quello che sto facendo ora è scorrere l'elenco delle DLL nella mia directory dei plugin e spingendoli a BuildManager come sopra.

L'unica limitazione qui è che non è possibile rimuovere gli assembly o cancellare la cache in modo dinamico. L'unico modo in cui ho trovato di farlo è quello di aggiungere un assembly precedentemente sconosciuto agli assiemi di riferimento e alle dipendenze di compilazione. Sto sperimentando emettendo dinamicamente un nuovo assembly durante l'inizializzazione della pre-applicazione in modo che io possa sempre, in modo efficace, svuotare la cache e rimuovere i plug-in precedentemente inclusi falsificando un nuovo assembly.

Spero che questo aiuti qualcun altro là fuori.

Cheers.

+0

Grazie mille per le vostre domande e risposte dettagliate. Una cosa che non capisco è perché non stai usando una fabbrica di controller personalizzata (vedi 'ControllerBuilder.Current.SetControllerFactory')? Questo ti permetterebbe di avere il pieno controllo su quando cedere quale controller, vero? – Dejan

1

Sembra che questo problema:

MVC utilizza assemblaggio qualificato nome del tipo del motore al fine di disambiguare le voci della cache della vista provenienti da diversi motori di vista. Quindi è impossibile avere più di un oggetto PrecompiledMvcEngine (come quando si sono precompilate le viste in più di un assieme). Il problema può essere risolto creando una classe derivata diversa da PrecompiledMvcEngine per ogni assembly. O creando una singola classe derivata generica parametrizzata con un certo tipo dall'assieme.

L'articolo è here.

+0

Grazie Nenad, ma in questo momento sto solo eseguendo un plug-in, quindi ce n'è solo uno che prova a caricare. Questo può essere un fattore, ma ho anche avuto 2 plugin in esecuzione contemporaneamente, quindi non sono sicuro che lo farà. Inoltre, questo non risolve il problema che sto riscontrando nel fatto che il controller non è localizzato. Questa è la chiave per la generosità. Saluti. – MisterJames

+0

Puoi fornire maggiori dettagli sull'errore? traccia dello stack? – Nenad

+0

Vorrei che ci fossero. Tutto quello che sto ottenendo è 404 sul controller quando provo ad accedere a una vista che è. Ho Log4Net e ELMAH in corso e non c'è nulla che non va che sto prendendo (con la registrazione ad ogni passaggio). Ho più dettagli che aggiungerò alla domanda più tardi dopo aver parlato di alcune cose con alcuni membri del team Asp.Net questa settimana. – MisterJames

0

@MisterJames, dare un'occhiata a questo:

Asp.Net Mvc Pluggable Application

Spero sia utile.

+0

Felipe, sta usando le aree del progetto all'interno della stessa soluzione ed è un approccio completamente diverso. I miei bisogni sono di avere soluzioni autonome per i plugin. Lo valuterò più dettagliatamente per vedere se ci sono indizi, ma così com'è non credo che aiuterà nel mio scenario. – MisterJames