2008-08-21 5 views
48

Ich versuche, eine Webanwendung zu erstellen, wo ich in der Lage sein soll, separate Assemblys zu verbinden. Ich benutze MVC Vorschau 4 kombiniert mit Unity für die Abhängigkeitsinjektion, die ich verwende, um die Controller aus meinen Plugins zu erstellen. Ich verwende WebForms (Standard-ASPX) als meine Ansichts-Engine.Ansichten in separaten Assemblys in ASP.NET MVC

Wenn ich eine Ansicht verwenden möchte, bleibe ich bei denen, die im Kernprojekt definiert sind, wegen der dynamischen Kompilierung des ASPX-Teils. Ich suche nach einer geeigneten Möglichkeit, ASPX-Dateien in einer anderen Assembly zu umschließen, ohne den gesamten Bereitstellungsschritt durchlaufen zu müssen. Fehle ich etwas Offensichtliches? Oder sollte ich meine Ansichten programmatisch erstellen?


Update: Ich habe die angenommene Antwort geändert. Obwohl Dales Antwort sehr gründlich ist, habe ich mich für die Lösung mit einem anderen Anbieter für virtuelle Pfade entschieden. Es funktioniert wie ein Zauber und benötigt insgesamt nur etwa 20 Zeilen Code.

+0

zu starten Ist die virtuellen Pfad Provider nach wie vor mit dem Routing in mvc.net arbeiten? – jmcd

+0

@jmcd: es sieht so aus. –

+0

Gibt es einen Beispielcode, den Sie posten können? –

Antwort

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

Stellen Sie die ‚Copy to output‘ Eigentum Ihrer Ansicht zu ‚Copy immer‘

+0

Anstatt eine WebFormviewEngine zu erstellen, können Sie dasselbe tun, indem Sie die RazorViewEngine wie folgt aufrufen: RazorViewEngine engine = ViewEngines.Engines.OfType (). First(); und dann fügen Sie einfach die zusätzlichen Pfade zu den genannten Eigenschaften hinzu. – CSharpie

2

Zusatz für alle Sie, die noch nach dem heiligen Gral suchen: Ich bin gekommen, ein wenig näher zu finden, es, wenn Sie nicht zu sehr an die Webforms Viewengine gebunden sind.

Ich habe vor kurzem die Spark-Viewengine ausprobiert. Abgesehen davon, dass ich total genial bin und ich nicht zu Webforms zurückkehren würde, selbst wenn ich bedroht wäre, bietet es auch einige sehr schöne Haken für die Modularität einer Anwendung. Das Beispiel in ihren Dokumenten verwendet Windsor als IoC-Container, aber ich kann mir nicht vorstellen, dass es viel schwieriger ist, wenn Sie einen anderen Ansatz verfolgen möchten.

29

Es hat viel zu lange gedauert, bis die verschiedenen Partial Samples richtig funktionierten. Hier ist der vollständige Code, um Ansichten aus einem Views-Ordner in einer gemeinsam genutzten Bibliothek zu erhalten als eingebettete Ressourcen erstellen. Es wird nur die eingebettete Datei verwenden, wenn die übliche Datei nicht existiert.

Die erste Zeile der Application_Start:

HostingEnvironment.RegisterVirtualPathProvider(new EmbeddedViewPathProvider()); 

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

    } 

} 

Der letzte Schritt, um es Arbeit ist, dass die Wurzel Web.Config die richtigen Einstellungen enthalten muss stark typisierte MVC zu analysieren Sichten, da die im Sichtenordner nicht verwendet wird:

<pages 
    validateRequest="false" 
    pageParserFilterType="System.Web.Mvc.ViewTypeParserFilter, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
    pageBaseType="System.Web.Mvc.ViewPage, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" 
    userControlBaseType="System.Web.Mvc.ViewUserControl, System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"> 
    <controls> 
    <add assembly="System.Web.Mvc, Version=2.0.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" namespace="System.Web.Mvc" tagPrefix="mvc" /> 
    </controls> 
</pages> 

Ein paar zusätzliche Schritte sind erforderlich, um mit Mono arbeiten zu können. Zuerst müssen Sie GetDirectory, da alle Dateien in den Ansichten implementieren Ordnern geladen werden, wenn die Anwendung startet, anstatt nach Bedarf:

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

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

Schließlich stark typisierte Ansichten fast, aber nicht ganz perfekt funktionieren.Modell wird als nicht typisierte Objekt behandelt werden, so starke Typisierung, um wieder Sie benötigen, um Ihre gemeinsamen Ansichten mit so etwas wie

<% var Model2 = Model as IEnumerable<AppModel>; %> 
+2

Es ist auch wichtig, daran zu denken, die Build-Aktion für die Ansicht auf "Eingebettete Ressource" zu setzen. Der Ordner für die Ansicht sollte auch im Webprojekt vorhanden sein. In meinem Fall habe ich den Ordner Views/Shared/Dynamic.cshtml des Assembly-Projekts angezeigt und muss im Webprojekt Views/Shared erstellen, das auf die Assembly verweist. –