【问题标题】:ASP.NET using embedded resources in BundlingASP.NET 在 Bundling 中使用嵌入式资源
【发布时间】:2016-01-14 10:30:32
【问题描述】:

我正在尝试实现一种通用方法,以便为我的 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),这样就不需要将它包含在每个包含嵌入式资源的程序集中,并在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 中的嵌入资源。

我收到以下异常:

目录 'C:\webs\ProjectA\Scripts\SomeFolder\' 不存在。无法开始监控文件更改。

更新 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>

【问题讨论】:

  • 你的 Startup 类在哪里?在 ProjectA 中还是在 ProjectB 中?
  • ProjectA 是一个包含 VirtualPathProvider 的 NuGet 包。 ProjectB 另一个 NuGet 包,它提供了一些带有视图的功能(有多个)。 NuGet 包 ProjectB 依赖于 NuGetPackage ProjectA。应用程序安装 ProjectB NuGetPackages。因此 Startup 在 ProjectA 和 ProjectB 之外,但 ProjectA 和 ProjectB 可以挂钩到 PreApplicationStartMethod。
  • 似乎IsEmbeddedPath 方法返回false 而它应该返回true。您能告诉我们错误发生之前pathresourceName 的值吗?
  • 如果我调试IsEmbeddedPath 返回true 并且GetFile 也被调用并返回嵌入资源的流。
  • @PascalBerger IsEmbeddedPath 总是返回 true。通过查看调用堆栈,我们可以看到GetCacheDependency 被调用,然后基类GetCacheDependency 被调用,这意味着IsEmbeddedPath 返回false 一次。

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


【解决方案1】:

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

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

要安全地确定virtualPath 是否为bundle 目录,您可以使用BundleTable.Bundles 集合或使用约定(即:每个bundle 都应以~/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);
    }
}

【讨论】:

  • 谢谢。我试过这个,但 GetCacheDependency 没有调用 C:\webs\ProjectA\Scripts\SomeFolder` only for ~/Embedded/Js`。
  • @PascalBerger 当异常发生时,virtualPath 的值是多少?
  • 啊,对不起。我的文件/路径检测出错。如果我修复检查 ~/Embedded/Js 并返回 null 它可以工作。现在我只需要找到一种安全的方法来确定virtualPath 是目录还是文件:)
【解决方案2】:

关于以下错误

目录 'C:\webs\ProjectA\Scripts\SomeFolder\' 不存在。无法开始监控文件更改。

如果 SomeFolder 的所有资源文件都嵌入并因此在已发布站点中,则会发生这种情况 - 它没有创建此文件夹。

如果是捆绑包 - 它会在捆绑包创建时保留时间戳,并监视文件夹是否有任何文件更改以触发捆绑包文件中的更新。

这里 - SomeFolder 中没有要监视的文件 - 因为所有文件都是嵌入的。没有发现阻止文件夹监控 - 但通过处理这个特定的异常,它可以被忽略。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-10-30
    • 2010-09-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多