15

Я пытаюсь реализовать общий подход для предоставления возможности для разных сборок в моем веб-решении использовать встроенные файлы JavaScript и CSS из встроенных ресурсов. This blog post показывает технику с использованием VirtualPathProvider. Это прекрасно работает, но VirtualPathProvider должен быть включен в каждую сборку, содержащую встроенные ресурсы.ASP.NET с использованием встроенных ресурсов в Bundling

Я пытался усилить VirtualPathProvider с поста в блоге, так что сборка может быть передана в него, и он загружает ресурс из его сборки:

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

На этапе инициализации считывает все внедренные ресурсы пропущенных сборка:

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

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

И GetFile считывает содержимое из пройденной сборки:

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

Проверка ресурса внедренный ресурс этого узла, проверяя имена ресурсов для чтения в Initialize метода:

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

Я перенес EmbeddedVirtualPathProvider класс основного веб-проекта (Projecta), так что это Безразлично «т должен быть включен в каждой сборке, содержащей встроенные ресурсы и зарегистрировал его, используя следующий код в Global.asax:

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

в проекте, содержащий вложенные ресурсы (ProjectB) Я до сих пор создать следующий пакет в PostApplicationStartMethod:

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

Scripts/MyScript.js является внедренным ресурсом в ProjectB.

С этим я получаю следующее исключение:

Directory 'C: \ холсты \ ProjectA \ Scripts \ SomeFolder \' не существует. Не удалось начать мониторинг изменений файлов.

Update Полный след стека доступны в this Gist.

Обновление Также сам VirtualPathProvider работает нормально. Если я загрузить файл непосредственно, а не через узел и установить следующую запись в web.config он загружает встроенный JavaScript из ProjectB:

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

Где ваш класс Startup? В ProjectA или ProjectB? –

+0

ProjectA - это пакет NuGet, содержащий VirtualPathProvider. ProjectB еще один пакет NuGet, обеспечивающий некоторую функциональность с просмотрами (их несколько). Проект NuGet PackageB имеет зависимость от проекта NuGetPackage ProjectA. Приложения устанавливают ProjectB NuGetPackages. Поэтому запуск за пределами ProjectA и ProjectB, но ProjectA и ProjectB могут подключаться к PreApplicationStartMethod. –

+0

Кажется, что метод 'IsEmbeddedPath' возвращает' false', тогда как он должен возвращать 'true'. Не могли бы вы рассказать нам значение 'path' и' resourceName' до возникновения ошибки? –

ответ

1

При оптимизации ASP.net создания сверток его называют GetCacheDependency для виртуального каталога сценарий. Ваша реализация GetCacheDependency проверяет только виртуальный файл, для виртуального каталога он основан на базе VirtualPathProvider, которая проверяет наличие и отсутствие каталога.

Для решения этой проблемы вам необходимо проверить, является ли путь одного из ваших сценариев и возвращает null для GetCacheDependency.

Чтобы безопасно определить, является ли каталог virtualPath каталогом пакетов, вы можете использовать коллекцию BundleTable.Bundles или использовать соглашение (то есть: каждый комплект должен начинаться с ~/Embedded).

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

Спасибо. Я попробовал это, но 'GetCacheDependency' не вызывается для' C: \ webs \ ProjectA \ Scripts \ SomeFolder \ 'только для' ~/Embedded/Js'. –

+0

@PascalBerger Когда возникает исключение, что такое значение 'virtualPath'? –

+0

Ах, извините. Была ошибка в обнаружении файла/пути. Если я исправлю проверку на '~/Embedded/Js' и вернул' null', это сработает. Теперь мне нужно найти только безопасный способ определить, является ли 'virtualPath' каталогом или файлом :) –