2010-11-22 10 views
13

Attualmente sto sperimentando con aree caricate dinamicamente con ASP.NET MVC 3 RC. L'ho visto scritto in molti posti che non è quello a cui sono destinate le aree, e (almeno pre-MVC 2) non è possibile, per esempio ad esempio here.ASP.NET MVC 3 RC AreaRegistration.RegisterAllAreas() e assiemi caricati dinamicamente

Ma ancora! Dovrebbe essere possibile farlo funzionare, giusto? Ho creato una soluzione, aggiunto un progetto MVC 3, aggiunto un'area e alcuni contenuti. Tutto funziona bene. Ora ho creato un nuovo progetto di libreria di classi (nella stessa soluzione), aggiunto un riferimento ad esso dal progetto MVC, e ho iniziato a spostare le parti relative all'area nella libreria. Modificata la directory di output del progetto di libreria nella cartella area del progetto MVC e verificato che le viste e il loro web.config siano copiati nella cartella di output.

Dopo aver letto così tanto su come non si potevano avere aree esterne, è stato un po 'strano che questo funzionasse. Nessun problema affatto! Il problema inizia quando rimuovo il riferimento tra i progetti e carico la libreria nel codice. (Prima di chiamare AreaRegistration.RegisterAllAreas().) Ora non funziona. Affatto.

Ho cercato un po 'nella sorgente per MVC 3, e il problema sembra essere con BuildManager.GetReferencedAssemblies() che viene utilizzato per ottenere gli assembly per cercare le implementazioni di AreaRegistration.

Ora, non ne sono sicuro al 100%, ma sembra che questo metodo guardi solo gli assembly presenti/referenziati in fase di compilazione, qualcuno può confermare se è effettivamente così?

Ho eseguito il debug attraverso questo, e quella chiamata-metodo effettivamente non trova l'assembly che ho caricato appena prima della chiamata. Potrebbe essere a causa di qualcos'altro che ho perso forse .. Qualche idea?

risposta

20

Il modo in cui funzionano le cose è un po 'complicato.

GetReferencedAssemblies include gruppi di riferimento, assiemi non caricati. Ciò include:

  • tutti gli assembly fa riferimento nel web.config vi applicazione (come System.Web.Mvc)
  • tutto ereditato da web.config root, che comprende le cose come System, System.Web e altri che non si deve aggiungere te stesso. (Puoi dare un'occhiata alla lista qui: C:\Windows\Microsoft.Net\Framework\v4.0.30319\web.config).
    Esso contiene anche una speciale * voce, che:
  • include tutto nella cartella del tuo sito bin

Così ora prendere la v1 app (tutto in una singola applicazione). Tutto funziona perché il codice dell'applicazione viene compilato nella cartella bin che viene automaticamente inclusa. Inoltre, tutte le viste dell'area ecc sono nell'applicazione stessa, quindi sono accessibili.

ora in App v2 (progetto diverso con un riferimento proj-to-proj e un compito di generazione personalizzata che copia i punti di vista per la posizione giusta nella vostra applicazione principale) tutto funziona ancora, perché di default un proj-to i riferimenti -proj indicano che il file binario della libreria di classi viene copiato nella cartella bin della tua app. Quindi, con le regole precedenti, il prefisso viene ancora caricato correttamente. Il fatto che tu abbia impostato il percorso di output della libreria in una posizione all'interno della cartella Aree dell'app principale non fa davvero la differenza: ti ritroverai con due copie del file binario.

ora in App v3 (senza prog-proj ref, assemblaggio biblioteca zona caricato manualmente) l'assembly di libreria viene caricato troppo tardi. Nel momento in cui il codice viene eseguito, il set di assembly referenziati è già stato bloccato e non può più essere modificato.

C'è un modo per eseguire il codice e aggiungere elementi alla lista delle assemblee registrati: è possibile farlo utilizzando il metodo AddReferencedAssembly che deve essere invocate da un PreApplicationStartMethodAttribute method.

Naturalmente avete ancora a che fare con il modo di gestire i file di visualizzazione. Il modo in cui si dispone attualmente di esso istituito è praticamente lo stesso di avere il punto di vista del ricorso principale (dal momento che effettivamente vengono copiati nella posizione giusta).

+0

risposta perfetta, grazie mil! Sì, lo so che al momento è praticamente lo stesso di avere un solo progetto, ma volevo separarli lentamente e vedere dove colpivo i problemi mentre andavo, invece di tutti i problemi contemporaneamente :) I'm considerando la possibilità di compilare le viste come risorse nell'assemblaggio e realizzare le mie implementazioni di percorso/file virtuali, a meno che non sia un problema di prestazioni troppo elevato .. Come ho detto, sto solo giocando in questo momento per vedere cosa è possibile e cosa no ! Grazie ancora! – Robin

+0

Potrebbe essere utile controllare tutte le viste nel proprio assieme area come risorse incorporate e osservare l'implementazione di un provider di percorsi virtuali. Questo è quello che ho fatto per realizzare gli assembly drag'n drop che ho potuto inserire nella directory bin del sito principale. –

+0

@Joshua Hayes - mi puoi inviare la vostra soluzione zone pluggable? –

6

1 - Separare voi MVC Aree in differrent MVC progetti da compilare nelle proprie assemblee separate

2 - Aggiungi questo alla classe AssemblyInfo.cs, di chiamare un metodo quando l'applicazione viene caricata

[assembly: PreApplicationStartMethod(typeof(PluginAreaBootstrapper), "Init")] 

3 - Ecco ciò che il metodo Init sembra quando viene invocato durante il carico

public class PluginAreaBootstrapper 
{ 
    public static readonly List<Assembly> PluginAssemblies = new List<Assembly>(); 

    public static List<string> PluginNames() 
    { 
     return PluginAssemblies.Select(
      pluginAssembly => pluginAssembly.GetName().Name) 
      .ToList(); 
    } 

    public static void Init() 
    { 
     var fullPluginPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Areas"); 

     foreach (var file in Directory.EnumerateFiles(fullPluginPath, "*Plugin*.dll")) 
      PluginAssemblies.Add(Assembly.LoadFile(file)); 

     PluginAssemblies.ForEach(BuildManager.AddReferencedAssembly); 
    } 
} 

4 - Aggiungere un RazorViewEngine personalizzato

public class PluginRazorViewEngine : RazorViewEngine 
{ 
    public PluginRazorViewEngine() 
    { 
     AreaMasterLocationFormats = new[] 
     { 
      "~/Areas/{2}/Views/{1}/{0}.cshtml", 
      "~/Areas/{2}/Views/{1}/{0}.vbhtml", 
      "~/Areas/{2}/Views/Shared/{0}.cshtml", 
      "~/Areas/{2}/Views/Shared/{0}.vbhtml" 
     }; 

     AreaPartialViewLocationFormats = new[] 
     { 
      "~/Areas/{2}/Views/{1}/{0}.cshtml", 
      "~/Areas/{2}/Views/{1}/{0}.vbhtml", 
      "~/Areas/{2}/Views/Shared/{0}.cshtml", 
      "~/Areas/{2}/Views/Shared/{0}.vbhtml" 
     }; 

     var areaViewAndPartialViewLocationFormats = new List<string> 
     { 
      "~/Areas/{2}/Views/{1}/{0}.cshtml", 
      "~/Areas/{2}/Views/{1}/{0}.vbhtml", 
      "~/Areas/{2}/Views/Shared/{0}.cshtml", 
      "~/Areas/{2}/Views/Shared/{0}.vbhtml" 
     }; 

     var partialViewLocationFormats = new List<string> 
     { 
      "~/Views/{1}/{0}.cshtml", 
      "~/Views/{1}/{0}.vbhtml", 
      "~/Views/Shared/{0}.cshtml", 
      "~/Views/Shared/{0}.vbhtml" 
     }; 

     var masterLocationFormats = new List<string> 
     { 
      "~/Views/{1}/{0}.cshtml", 
      "~/Views/{1}/{0}.vbhtml", 
      "~/Views/Shared/{0}.cshtml", 
      "~/Views/Shared/{0}.vbhtml" 
     }; 

     foreach (var plugin in PluginAreaBootstrapper.PluginNames()) 
     { 
      masterLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/{1}/{0}.cshtml"); 
      masterLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml"); 
      masterLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/Shared/{1}/{0}.cshtml"); 
      masterLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/Shared/{1}/{0}.vbhtml"); 

      partialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/{1}/{0}.cshtml"); 
      partialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml"); 
      partialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/Shared/{0}.cshtml"); 
      partialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/Shared/{0}.vbhtml"); 

      areaViewAndPartialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/{1}/{0}.cshtml"); 
      areaViewAndPartialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Views/{1}/{0}.vbhtml"); 
      areaViewAndPartialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Areas/{2}/Views/{1}/{0}.cshtml"); 
      areaViewAndPartialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Areas/{2}/Views/{1}/{0}.vbhtml"); 
      areaViewAndPartialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Areas/{2}/Views/Shared/{0}.cshtml"); 
      areaViewAndPartialViewLocationFormats.Add(
       "~/Areas/" + plugin + "/Areas/{2}/Views/Shared/{0}.vbhtml"); 
     } 

     ViewLocationFormats = partialViewLocationFormats.ToArray(); 
     MasterLocationFormats = masterLocationFormats.ToArray(); 
     PartialViewLocationFormats = partialViewLocationFormats.ToArray(); 
     AreaPartialViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray(); 
     AreaViewLocationFormats = areaViewAndPartialViewLocationFormats.ToArray(); 
    } 
} 

5 - Registrare i Aree dal diverso MVC (Regione) Progetti

namespace MvcApplication8.Web.MyPlugin1 
{ 
    public class MyPlugin1AreaRegistration : AreaRegistration 
    { 
     public override string AreaName 
     { 
      get { return "MyPlugin1"; } 
     } 

     public override void RegisterArea(AreaRegistrationContext context) 
     { 
      context.MapRoute(
       "MyPlugin1_default", 
       "MyPlugin1/{controller}/{action}/{id}", 
       new {action = "Index", id = UrlParameter.Optional} 
       ); 
     } 
    } 
} 

Sourcecode e ulteriori riferimenti può può essere trovato qui: http://blog.longle.io/2012/03/29/building-a-composite-mvc3-application-with-pluggable-areas

+0

Eh, guarda che, molto bello! Dovrò fare una prova ad un certo punto, grazie! – Robin

+0

apprezza i punti Robin! – LeLong37

+0

State cercando qualcosa di simile per giorni, grazie! – rossipedia