c# - ASP.NET 在捆绑中使用嵌入式资源

标签 c# asp.net-mvc embedded-resource bundling-and-minification virtualpathprovider

我正在尝试实现一种通用方法,以便为我的 Web 解决方案中的不同程序集提供使用来自嵌入式资源的嵌入式 JavaScript 和 CSS 文件的可能性。 This blog post展示了一种使用 VirtualPathProvider 的技术。这工作正常,但 VirtualPathProvider 需要包含在每个包含嵌入式资源的程序集中。

我尝试从博文中增强 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 类移动到主 Web 项目 (ProjectA) 中,这样它就不需要包含在每个包含嵌入式资源的程序集中,并使用 中的以下代码注册它>全局.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:\webs\ProjectA\Scripts\SomeFolder\' does not exist. Failed to start monitoring file changes.

更新 this Gist 中可用的完整堆栈跟踪.

更新 此外,VirtualPathProvider 本身似乎工作正常。如果我直接加载文件而不是通过包加载文件并在 web.config 中设置以下条目,它会从 ProjectB 加载嵌入式 javascript:

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

最佳答案

当 ASP.net 优化创建包时,它会为脚本的虚拟目录调用 GetCacheDependency。您的 GetCacheDependency 实现仅检查虚拟文件,对于虚拟目录,它依赖于基础 VirtualPathProvider,后者检查目录是否存在且失败。

要解决此问题,您必须检查该路径是否是您的某个脚本的目录,并为 GetCacheDependency 返回 null。

要安全地确定 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);
    }
}

关于c# - ASP.NET 在捆绑中使用嵌入式资源,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34787206/

相关文章:

c# - 使类 IEnumerable

c# - WPF : Accessing an embedded resource in XAML

rust - 当包含在 include_bytes 宏中的文件被更改时,我如何告诉 Cargo 重建?

c# - SetWindowLong 启用/禁用单击表单

c# - 在 Antlr4、C# 中,错误处理未按预期触发

c# - 将变量添加到布局页面 MVC

jQuery 工具 : How to prevent a submit event from a form that is rendered dynamically into a page

java - 以水平顺序显示一系列图片(带图像的 JPanels)

c# - 如何从另一个应用程序打开 WPF 窗口

c# - string message = Regex.Replace(message, "\\", "") 有问题;