ASP.NET using embedded resources in Bundling
<p>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. <a href="http://weblogs.asp.net/imranbaloch/asp-net-bundling-and-minification-and-embedded-resources" rel="nofollow noreferrer">This blog post</a> shows a technique using a VirtualPathProvider. This works fine, but the VirtualPathProvider needs to be included in each assembly containing embedded resources.</p>

<p>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:</p>

<pre><code>public EmbeddedVirtualPathProvider(VirtualPathProvider previous, Assembly assembly)
{
    this.previous = previous;
    this.assembly = assembly;
}
</code></pre>

<p>On initialization it reads all embedded resources from the passed assembly:</p>

<pre><code>protected override void Initialize()
{
    base.Initialize();

    this.assemblyResourceNames = this.assembly.GetManifestResourceNames();
    this.assemblyName = this.assembly.GetName().Name;
}
</code></pre>

<p>And the <code>GetFile</code>reads the content from the passed assembly:</p>

<pre><code>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);
}
</code></pre>

<p>Checking if resource is an embedded resource of this assembly is by checking the resource names read in the <code>Initialize</code> method:</p>

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

<p>I moved the <code>EmbeddedVirtualPathProvider</code> 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 <code>Global.asax</code>:</p>

<pre><code>HostingEnvironment.RegisterVirtualPathProvider(
    new EmbeddedVirtualPathProvider(
        HostingEnvironment.VirtualPathProvider,
        typeof(ProjectB.SomeType).Assembly));
</code></pre>

<p>In the project containing the embedded resources (ProjectB) I still create the following bundle in a <code>PostApplicationStartMethod</code>:</p>

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

<p><code>Scripts/MyScript.js</code> is the embedded resource in ProjectB.</p>

<p>With this I receive the following exception:</p>

<blockquote>
  <p>Directory 'C:\webs\ProjectA\Scripts\SomeFolder\' does not exist. Failed to start monitoring file changes.</p>
</blockquote>

<p><strong>Update</strong>
Full stack trace available in <a href="https://gist.github.com/pascalberger/e1c84c8d8bb966cbe666" rel="nofollow noreferrer">this Gist</a>.</p>

<p><strong>Update</strong>
Also the VirtualPathProvider itself seems to work fine. If I load the file directly and not through the bundle and set the following entry in the <code>web.config</code> it loads the embedded javascript from ProjectB:</p>

<pre><code>&lt;system.webServer&gt;
  &lt;handlers&gt;
    &lt;add name="MyStaticFileHandler" path="*.js" verb="GET,HEAD" type="System.Web.StaticFileHandler"/&gt;
  &lt;/handlers&gt;
&lt;/system.webServer&gt;
</code></pre>