简体   繁体   English

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

[英]ASP.NET using embedded resources in Bundling

I'm trying to implement a generic approach for providing the possibility for different assemblies in my web solution to use embedded JavaScript and CSS files from embedded resources.我正在尝试实现一种通用方法,以便为我的 Web 解决方案中的不同程序集提供使用来自嵌入式资源的嵌入式 JavaScript 和 CSS 文件的可能性。 This blog post shows a technique using a VirtualPathProvider. 这篇博文展示了一种使用 VirtualPathProvider 的技术。 This works fine, but the VirtualPathProvider needs to be included in each assembly containing embedded resources.这工作正常,但 VirtualPathProvider 需要包含在每个包含嵌入式资源的程序集中。

I tried to enhance the VirtualPathProvider from the blog post, so that an assembly can be passed into it and it loads the resource from its assembly:我试图从博客文章中增强 VirtualPathProvider,以便可以将程序集传递给它并从其程序集中加载资源:

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

On initialization it reads all embedded resources from the passed assembly:在初始化时,它从传递的程序集中读取所有嵌入的资源:

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

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

And the GetFile reads the content from the passed assembly: 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);
}

Checking if resource is an embedded resource of this assembly is by checking the resource names read in the Initialize method:通过检查Initialize方法中读取的资源名称来检查资源是否是该程序集的嵌入资源:

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

I moved the EmbeddedVirtualPathProvider class to the main web project (ProjectA), so that it doesn't need to be included in each assembly containing embedded resources and registered it using the following code in Global.asax :我将EmbeddedVirtualPathProvider类移动到主 Web 项目 (ProjectA),这样它就不需要包含在每个包含嵌入式资源的程序集中,并在Global.asax中使用以下代码注册它:

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

In the project containing the embedded resources (ProjectB) I still create the following bundle in a PostApplicationStartMethod :在包含嵌入式资源的项目 (ProjectB) 中,我仍然在PostApplicationStartMethod中创建以下包:

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

Scripts/MyScript.js is the embedded resource in ProjectB. Scripts/MyScript.js是 ProjectB 中的嵌入式资源。

With this I receive the following exception:有了这个,我收到以下异常:

Directory 'C:\webs\ProjectA\Scripts\SomeFolder\' does not exist.目录“C:\webs\ProjectA\Scripts\SomeFolder\”不存在。 Failed to start monitoring file changes.无法开始监视文件更改。

Update Full stack trace available in this Gist .更新此 Gist中可用的完整堆栈跟踪。

Update Also the VirtualPathProvider itself seems to work fine.更新VirtualPathProvider 本身似乎也能正常工作。 If I load the file directly and not through the bundle and set the following entry in the web.config it loads the embedded javascript from ProjectB:如果我直接而不是通过包加载文件并在web.config中设置以下条目,它会从 ProjectB 加载嵌入式 javascript:

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

When ASP.net optimization create the bundle it call GetCacheDependency for the virtual directory of the script.当 ASP.net 优化创建包时,它会为脚本的虚拟目录调用GetCacheDependency Your GetCacheDependency implementation only check virtual file, for virtual directory it relies on the base VirtualPathProvider which check if directory exists and failed.您的GetCacheDependency实现仅检查虚拟文件,对于虚拟目录,它依赖于基础VirtualPathProvider ,后者检查目录是否存在且失败。

To solve this issue, you have to check if the path is a directory of one of your script and return null for the GetCacheDependency .要解决此问题,您必须检查路径是否是您的脚本之一的目录,并为GetCacheDependency返回 null。

To safely determine if virtualPath is a bundle directory, you can use the BundleTable.Bundles collection or using a convention (ie: every bundle should starts with ~/Embedded).要安全地确定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);
    }
}

Regarding below error关于以下错误

Directory 'C:\webs\ProjectA\Scripts\SomeFolder\' does not exist.目录“C:\webs\ProjectA\Scripts\SomeFolder\”不存在。 Failed to start monitoring file changes.无法开始监视文件更改。

This happens specifically if all resource files of the SomeFolder are embedded and thus in published site - it does not have this folder created.如果嵌入了 SomeFolder 的所有资源文件并因此在已发布的站点中,则会特别发生这种情况 - 它没有创建此文件夹。

In case of bundle - it keeps timestamp when the bundle is created and it monitors the folder for any file change to trigger update in the bundle file.对于 bundle - 它在创建 bundle 时保留时间戳,并监视文件夹中的任何文件更改以触发 bundle 文件中的更新。

Here - no files in the SomeFolder to monitor - as all are embedded.这里 - SomeFolder 中没有要监控的文件 - 因为所有文件都是嵌入的。 Didn't find to prevent the folder monitoring - but by handling this specific exception, it can be ignored.没有找到阻止文件夹监视的方法——但是通过处理这个特定的异常,它可以被忽略。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM