15

Ich versuche, einen generischen Ansatz zu implementieren, um die Möglichkeit für verschiedene Assemblies in meiner Weblösung zu bieten, eingebettete JavaScript- und CSS-Dateien aus eingebetteten Ressourcen zu verwenden. This blog post zeigt eine Technik, die einen VirtualPathProvider verwendet. Dies funktioniert einwandfrei, aber der VirtualPathProvider muss in jeder Assembly enthalten sein, die eingebettete Ressourcen enthält.ASP.NET mit eingebetteten Ressourcen in Bundling

Ich versuchte, die VirtualPathProvider aus der Blog-Post zu verbessern, so dass eine Baugruppe hinein geführt werden kann und es die Ressource aus seiner Assembly lädt:

public EmbeddedVirtualPathProvider(VirtualPathProvider previous, Assembly assembly) 
{ 
    this.previous = previous; 
    this.assembly = assembly; 
} 

Bei der Initialisierung liest sie alle eingebetteten Ressourcen aus dem übergebenen Montag:

protected override void Initialize() 
{ 
    base.Initialize(); 

    this.assemblyResourceNames = this.assembly.GetManifestResourceNames(); 
    this.assemblyName = this.assembly.GetName().Name; 
} 

Und die GetFile liest den Inhalt aus dem übergebenen assembly:

public override VirtualFile GetFile(string virtualPath) 
{ 
    if (IsEmbeddedPath(virtualPath)) 
    { 
     if (virtualPath.StartsWith("~", System.StringComparison.OrdinalIgnoreCase)) 
     { 
      virtualPath = virtualPath.Substring(1); 
     } 

     if (!virtualPath.StartsWith("/", System.StringComparison.OrdinalIgnoreCase)) 
     { 
      virtualPath = string.Concat("/", virtualPath); 
     } 

     var resourceName = string.Concat(this.assembly.GetName().Name, virtualPath.Replace("/", ".")); 
     var stream = this.assembly.GetManifestResourceStream(resourceName); 

     if (stream != null) 
     { 
      return new EmbeddedVirtualFile(virtualPath, stream); 
     } 
     else 
     { 
      return _previous.GetFile(virtualPath); 
     } 
    } 
    else 
     return _previous.GetFile(virtualPath); 
} 

prüft Ressource eine eingebettete Ressource dieser Anordnung ist durch die Ressourcennamen in der Initialize Methode lesen Überprüfung:

private bool IsEmbeddedPath(string path) 
{ 
    var resourceName = string.Concat(this.assemblyName, path.TrimStart('~').Replace("/", ".")); 
    return this.assemblyResourceNames.Contains(resourceName, StringComparer.OrdinalIgnoreCase); 
} 

Ich zog die EmbeddedVirtualPathProvider Klasse auf die Haupt Webprojekt (ProjectA), so dass es doesn ‚müssen t in jeder Baugruppe enthält eingebettete Ressourcen einbezogen werden und registriert es den folgenden Code in Global.asax mit:

HostingEnvironment.RegisterVirtualPathProvider(
    new EmbeddedVirtualPathProvider(
     HostingEnvironment.VirtualPathProvider, 
     typeof(ProjectB.SomeType).Assembly)); 

im Projekt die eingebetteten Ressourcen enthält (ProjectB) ich immer noch das folgende Bündel in einerstellen:

BundleTable.Bundles.Add(new ScriptBundle("~/Embedded/Js") 
    .Include("~/Scripts/SomeFolder/MyScript.js") 
); 

Scripts/MyScript.js ist die eingebettete Ressource in ProjectB.

Damit erhalte ich die folgende Ausnahme:

Verzeichnis 'C: \ webs \ ProjectA \ Scripts \ SomeFolder \' existiert nicht. Fehler beim Starten der Überwachung von Dateiänderungen.

aktualisieren Voll Stack-Trace in this Gist verfügbar.

Update Auch der VirtualPathProvider selbst scheint gut zu funktionieren. Wenn ich die Datei direkt und nicht durch das Bündel laden und legen Sie den folgenden Eintrag in der web.config lädt es das eingebettete Javascript aus ProjectB:

<system.webServer> 
    <handlers> 
    <add name="MyStaticFileHandler" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler"/> 
    </handlers> 
</system.webServer> 
+0

Wo ist Ihre Startup-Klasse? In ProjectA oder in ProjectB? –

+0

ProjectA ist ein NuGet-Paket, das den VirtualPathProvider enthält. ProjectB ein anderes NuGet-Paket mit einigen Funktionen mit Ansichten (es gibt mehrere davon). Das NuGet-Paket ProjectB ist von dem NuGetPackage-ProjektA abhängig. Anwendungen installieren die ProjectB NuGetPackages. Daher befindet sich der Startvorgang außerhalb von ProjectA und ProjectB, aber ProjectA und ProjectB können sich in PreApplicationStartMethod einklinken. –

+0

Es scheint, dass die 'IsEmbeddedPath'-Methode' false' zurückgibt, während sie 'true' zurückgeben sollte. Können Sie uns den Wert von 'path' und' resourceName' mitteilen, bevor der Fehler auftrat? –

Antwort

1

Wenn ASP.net Optimierung des Bündels erstellen nennen es GetCacheDependency für das virtuelle Verzeichnis von das Skript. Ihre GetCacheDependency Implementierung prüft nur die virtuelle Datei, für das virtuelle Verzeichnis ist sie auf die Basis VirtualPathProvider angewiesen, die überprüft, ob das Verzeichnis existiert und fehlgeschlagen ist.

Um dieses Problem zu beheben, müssen Sie überprüfen, ob der Pfad ein Verzeichnis eines Ihrer Skripts ist und null für GetCacheDependency zurückgeben.

Um sicher zu bestimmen, ob virtualPath ein Bündelverzeichnis ist, können Sie die BundleTable.Bundles Sammlung verwenden oder eine Konvention verwenden (dh jedes Bündel sollte mit ~/Embedded beginnen).

public override CacheDependency GetCacheDependency(
    string virtualPath, 
    IEnumerable virtualPathDependencies, 
    DateTime utcStart) 
{ 
    // if(virtualPath.StartsWith("~/Embedded")) 
    if(BundleTables.Bundles.Any(b => b.Path == virtualPath)) 
    { 
     return null; 
    } 
    if (this.IsEmbeddedPath(virtualPath)) 
    { 
     return null; 
    } 
    else 
    { 
     return this._previous 
        .GetCacheDependency(virtualPath, virtualPathDependencies, utcStart); 
    } 
} 
+0

Danke. Ich habe es versucht, aber 'GetCacheDependency' wird nicht für' C: \ webs \ ProjectA \ Scripts \ SomeFolder \ 'nur für' ~/Embedded/Js' aufgerufen. –

+0

@PascalBerger Wenn die Ausnahme auftritt, was ist der Wert von 'virtualPath'? –

+0

Ah, tut mir leid. War ein Fehler in meiner Datei-/Pfaderkennung. Wenn ich nach "~/Embedded/Js" suche und "null" zurückgebe, funktioniert es. Jetzt muss ich nur einen sicheren Weg finden, um festzustellen, ob 'virtualPath' ein Verzeichnis oder eine Datei ist :) –