ASP.NET在Bundling中使用嵌入式资源

时间:2016-01-14 10:30:32

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

我试图实现一种通用方法,为我的网络解决方案中的不同程序集提供使用嵌入式资源中嵌入的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),因此它不需要包含在包含嵌入资源的每个程序集中,并使用{{1中的以下代码注册它}}:

Global.asax

在包含嵌入资源(ProjectB)的项目中,我仍然在HostingEnvironment.RegisterVirtualPathProvider( new EmbeddedVirtualPathProvider( HostingEnvironment.VirtualPathProvider, typeof(ProjectB.SomeType).Assembly)); 中创建以下包:

PostApplicationStartMethod

BundleTable.Bundles.Add(new ScriptBundle("~/Embedded/Js") .Include("~/Scripts/SomeFolder/MyScript.js") ); 是ProjectB中的嵌入式资源。

有了这个,我收到以下例外:

  

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

更新 this Gist中提供了完整堆栈跟踪。

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

web.config

2 个答案:

答案 0 :(得分:1)

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

答案 1 :(得分:1)

关于以下错误

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

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

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

在这里-SomeFolder中没有要监视的文件-因为所有文件都是嵌入式的。找不到阻止文件夹监视的方法-但是通过处理此特定异常,可以将其忽略。