Search code examples
asp.net-mvc-3iis-7.5embedded-resource

How do I serve assembly embedded resources from MVC 3 and IIS7?


I'm developing an MVC 3 application with a "plugin" functionality. The plugins are C# dll's with all required resources (css, images and scripts) embedded.

I've used the "MvcRazorClassGenerator" Visual Studio extension to create precompiled views.

I retrieve the embedded resource using the following code:

public FileStreamResult EmbeddedResource(string pluginName, string resourceName)
{
  Assembly assembly = PluginCache.GetAssembly(pluginName);

  if (assembly != null)
  {
    string tempResourceName =
      assembly.GetManifestResourceNames()
              .ToList().FirstOrDefault(f => f.EndsWith(resourceName));

    return new FileStreamResult(
                  assembly.GetManifestResourceStream(tempResourceName), 
                  GetMIMEType(tempResourceName));
  }  

  return null;
}

In the views I have following code to access resources:

@Url.Content("/Common/EmbeddedResource/PluginName/[AssemblyNamespace].Content.Images.blank.gif")

All is working fine while I am in the development environment, all resources are loaded and displayed correctly, but when deploying, the nightmare is started.

IIS 7.5 keeps searching for a static file called "/Common/EmbeddedResource/PluginName/[AssemblyNamespace].Content.Images.blank.gif" and not the embedded file, giving me the 404 error for all embedded resources.

I've tried installing a hot fix mentioned by a question on this site and changing config files but the resources are not loaded.

I'm trying to deploy to Windows 2008 Server R2 SP1 64bit.


Solution

  • The problem may be the use of

    Url.Content("/Common/EmbeddedResource/PluginName/[AssemblyNamespace].Content.Images.blank.gif")
    

    I have the same set up but since the content is served from an action I use

    @Url.Action("EmbeddedResource", "EmbeddedResources", new { pluginName = "PluginName", resourceName = "MyProject.Scripts.MyScript.js" })
    

    I then set up a route

    routes.MapRoute(
        "EmbeddedResources",
        "EmbeddedResources/{pluginName}/{resourceName}",
        new { controller = "EmbeddedResources", action = "EmbeddedResource", pluginName = "DefaultPluginName", resourceName = UrlParameter.Optional });
    

    Which results in script references like so

    <script type='text/javascript' src='/EmbeddedResources/PluginName/MyProject.Scripts.MyScript.js'></script>
    

    If you are taking this approach, you may also want to minify the embedded files (for release builds). You can do this using the MSBuild task as detailed on this blog post.

     <Import Project="$(MSBuildExtensionsPath)\Microsoft\MicrosoftAjax\ajaxmin.tasks" />
      <PropertyGroup>
        <ResGenDependsOn>
          MinifyJavaScript;
          $(ResGenDependsOn)
        </ResGenDependsOn>
      </PropertyGroup>
      <PropertyGroup>
        <PostBuildEvent>
        </PostBuildEvent>
      </PropertyGroup>
      <Target Name="MinifyJavaScript" Condition=" '$(ConfigurationName)'=='Release' ">
        <Copy SourceFiles="@(EmbeddedResource)" DestinationFolder="$(IntermediateOutputPath)" Condition="'%(Extension)'=='.js'">
          <Output TaskParameter="DestinationFiles" ItemName="EmbeddedJavaScriptResource" />
        </Copy>
        <AjaxMin JSSourceFiles="@(EmbeddedJavaScriptResource)" JsSourceExtensionPattern="\.js$" JsTargetExtension=".js" CssSourceFiles="@(CSS)" CssSourceExtensionPattern="\.css$" CssTargetExtension=".css" JSLocalRenaming="CrunchAll" />
        <ItemGroup>
          <EmbeddedResource Remove="@(EmbeddedResource)" Condition="'%(Extension)'=='.js'" />
          <EmbeddedResource Include="@(EmbeddedJavaScriptResource)" />
          <FileWrites Include="@(EmbeddedJavaScriptResource)" />
        </ItemGroup>
      </Target>