Visualizzazioni in assembly separati in ASP.NET MVC

Sto cercando di creare un’applicazione web in cui voglio essere in grado di colbind assembly separati. Sto usando MVC preview 4 in combinazione con Unity per l’iniezione delle dipendenze, che uso per creare i controller dai miei gruppi di plugin. Sto usando WebForms (default aspx) come motore di visualizzazione.

Se voglio utilizzare una vista, sono bloccato su quelli definiti nel progetto principale, a causa della compilazione dynamic della parte ASPX. Sto cercando un modo corretto per racchiudere i file ASPX in un assembly diverso, senza dover passare attraverso l’intera fase di distribuzione. Mi manca qualcosa di ovvio? O dovrei ricorrere a creare le mie visualizzazioni a livello di codice?


Aggiornamento: ho cambiato la risposta accettata. Anche se la risposta di Dale è molto approfondita, ho optato per la soluzione con un provider di percorsi virtuali diverso. Funziona come un incantesimo e penso che in tutto il codice siano sufficienti solo 20 righe in codice.

Essenzialmente questo è lo stesso problema che hanno avuto gli utenti di WebForms e cercano di compilare i loro file ASCX UserControl in una DLL. Ho trovato questo http://www.codeproject.com/KB/aspnet/ASP2UserControlLibrary.aspx che potrebbe funzionare anche per te.

Mi ci è voluto troppo tempo per farlo funzionare correttamente dai vari campioni parziali, quindi ecco il codice completo necessario per ottenere viste da una cartella Views in una libreria condivisa strutturata come una normale cartella Views ma con tutto impostato per la compilazione come incorporato risorse. Utilizzerà il file incorporato solo se il file normale non esiste.

La prima riga di Application_Start:

HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider()); 

Il VirtualPathProvider

  public class EmbeddedVirtualFile : VirtualFile { public EmbeddedVirtualFile(string virtualPath) : base(virtualPath) { } internal static string GetResourceName(string virtualPath) { if (!virtualPath.Contains("/Views/")) { return null; } var resourcename = virtualPath .Substring(virtualPath.IndexOf("Views/")) .Replace("Views/", "OrangeGuava.Common.Views.") .Replace("/", "."); return resourcename; } public override Stream Open() { Assembly assembly = Assembly.GetExecutingAssembly(); var resourcename = GetResourceName(this.VirtualPath); return assembly.GetManifestResourceStream(resourcename); } } public class EmbeddedViewPathProvider : VirtualPathProvider { private bool ResourceFileExists(string virtualPath) { Assembly assembly = Assembly.GetExecutingAssembly(); var resourcename = EmbeddedVirtualFile.GetResourceName(virtualPath); var result = resourcename != null && assembly.GetManifestResourceNames().Contains(resourcename); return result; } public override bool FileExists(string virtualPath) { return base.FileExists(virtualPath) || ResourceFileExists(virtualPath); } public override VirtualFile GetFile(string virtualPath) { if (!base.FileExists(virtualPath)) { return new EmbeddedVirtualFile(virtualPath); } else { return base.GetFile(virtualPath); } } } 

Il passaggio finale per farlo funzionare è che la radice Web.Config deve contenere le giuste impostazioni per analizzare le viste MVC fortemente tipizzate, in quanto quella nella cartella delle visualizzazioni non verrà utilizzata:

      

Sono necessari un paio di passaggi aggiuntivi per farlo funzionare con Mono. Innanzitutto, è necessario implementare GetDirectory, poiché tutti i file nella cartella delle visualizzazioni vengono caricati all’avvio dell’app anziché in base alle esigenze:

 public override VirtualDirectory GetDirectory(string virtualDir) { Log.LogInfo("GetDirectory - " + virtualDir); var b = base.GetDirectory(virtualDir); return new EmbeddedVirtualDirectory(virtualDir, b); } public class EmbeddedVirtualDirectory : VirtualDirectory { private VirtualDirectory FileDir { get; set; } public EmbeddedVirtualDirectory(string virtualPath, VirtualDirectory filedir) : base(virtualPath) { FileDir = filedir; } public override System.Collections.IEnumerable Children { get { return FileDir.Children; } } public override System.Collections.IEnumerable Directories { get { return FileDir.Directories; } } public override System.Collections.IEnumerable Files { get { if (!VirtualPath.Contains("/Views/") || VirtualPath.EndsWith("/Views/")) { return FileDir.Files; } var fl = new List(); foreach (VirtualFile f in FileDir.Files) { fl.Add(f); } var resourcename = VirtualPath.Substring(VirtualPath.IndexOf("Views/")) .Replace("Views/", "OrangeGuava.Common.Views.") .Replace("/", "."); Assembly assembly = Assembly.GetExecutingAssembly(); var rfl = assembly.GetManifestResourceNames() .Where(s => s.StartsWith(resourcename)) .Select(s => VirtualPath + s.Replace(resourcename, "")) .Select(s => new EmbeddedVirtualFile(s)); fl.AddRange(rfl); return fl; } } } 

Infine, le visualizzazioni fortemente tipizzate quasi non funzioneranno perfettamente. Il modello verrà considerato come un object non tipizzato, quindi per ottenere una digitazione forte è necessario iniziare le visualizzazioni condivise con qualcosa di simile

 <% var Model2 = Model as IEnumerable; %> 
 protected void Application_Start() { WebFormViewEngine engine = new WebFormViewEngine(); engine.ViewLocationFormats = new[] { "~/bin/Views/{1}/{0}.aspx", "~/Views/Shared/{0}.aspx" }; engine.PartialViewLocationFormats = engine.ViewLocationFormats; ViewEngines.Engines.Clear(); ViewEngines.Engines.Add(engine); RegisterRoutes(RouteTable.Routes); } 

Imposta la proprietà ‘Copia in uscita’ della tua vista su ‘Copia sempre’

Un’aggiunta a tutti voi che state ancora cercando il Santo Graal: sono arrivato un po ‘più vicino a trovarlo, se non siete troppo attaccati al viewengine delle webform.

Recentemente ho provato il motore di visualizzazione Spark. Oltre ad essere totalmente fantastico e non vorrei tornare alle webform anche se sono stato minacciato, fornisce anche dei ganci molto belli per la modularità di un’applicazione. L’esempio nei loro documenti utilizza Windsor come un contenitore IoC, ma non posso immaginare che sia molto più difficile se si vuole prendere un altro approccio.