Posts
202
Comments
1112
Trackbacks
51
MVC Portable Areas – Static Files as Embedded Resources

This is the third post in a series related to build and deployment considerations as I’ve been exploring MVC Portable Areas:

In the last post, I walked through a convention for managing static files.  In this post I’ll discuss another approach to manage static files (e.g., images, css, js, etc.).  With this approach, you *also* compile the static files as embedded resources into the assembly similar to the *.aspx pages. Once again, you can set this to happen automatically by simply modifying your *.csproj file to include the desired extensions so you don’t have to remember every time you add a file:

   1:  <Target Name="BeforeBuild">
   2:    <ItemGroup>
   3:      <EmbeddedResource Include="**\*.aspx;**\*.ascx;**\*.gif;**\*.css;**\*.js" />
   4:    </ItemGroup>
   5:  </Target>

We now need a reliable way to serve up these static files that are embedded in the assembly. There are a couple of ways to do this but one way is to simply create a Resource controller whose job is dedicated to doing this.

   1:  public class ResourceController : Controller
   2:  {
   3:      public ActionResult Index(string resourceName)
   4:      {
   5:          var contentType = GetContentType(resourceName);
   6:          var resourceStream = Assembly.GetExecutingAssembly().GetManifestResourceStream(resourceName);
   7:          return this.File(resourceStream, contentType);
   8:      }
   9:   
  10:      private static string GetContentType(string resourceName)
  11:      {
  12:          var extention = resourceName.Substring(resourceName.LastIndexOf('.')).ToLower();
  13:          switch (extention)
  14:          {
  15:              case ".gif":
  16:                  return "image/gif";
  17:              case ".js":
  18:                  return "text/javascript";
  19:              case ".css":
  20:                  return "text/css";
  21:              default:
  22:                  return "text/html";
  23:          }
  24:      }
  25:  }

In order to use this controller, we need to make sure we’ve registered the route in our portable area registration (shown in lines 5-6):

   1:  public class WidgetAreaRegistration : PortableAreaRegistration
   2:  {
   3:      public override void RegisterArea(System.Web.Mvc.AreaRegistrationContext context, IApplicationBus bus)
   4:      {
   5:          context.MapRoute("ResourceRoute", "widget1/resource/{resourceName}",
   6:              new { controller = "Resource", action = "Index" });
   7:   
   8:          context.MapRoute("Widget1", "widget1/{controller}/{action}", new
   9:          {
  10:              controller = "Home",
  11:              action = "Index"
  12:          });
  13:   
  14:          RegisterTheViewsInTheEmbeddedViewEngine(GetType());
  15:      }
  16:   
  17:      public override string AreaName
  18:      {
  19:          get { return "Widget1"; }
  20:      }
  21:  }

In my previous post, we relied on a custom Url helper method to find the actual physical path to the static file like this:

   1:  <img src="<%: Url.AreaContent("/images/arrow.gif") %>" /> Hello World!

However, since we are now embedding the files inside the assembly, we no longer have to worry about the physical path. We can change this line of code to this:

   1:  <img src="<%: Url.Resource("Widget1.images.arrow.gif") %>" /> Hello World!

Note that I had to fully quality the resource name (with namespace and physical location) since that is how .NET assemblies store embedded resources. I also created my own Url helper method called Resource which looks like this:

   1:  public static string Resource(this UrlHelper urlHelper, string resourceName)
   2:  {
   3:      var areaName = (string)urlHelper.RequestContext.RouteData.DataTokens["area"];
   4:      return urlHelper.Action("Index", "Resource", new { resourceName = resourceName, area = areaName });
   5:  }

This method gives us the convenience of not having to know how to construct the URL – but just allowing us to refer to the resource name. The resulting html for the image tag is:

   1:  <img src="/widget1/resource/Widget1.images.arrow.gif" />

so we can always request any image from the browser directly. This is almost analogous to the WebResource.axd file but for MVC. What is interesting though is that we can encapsulate each one of these so that each area can have it’s own set of resources and they are easily distinguished because the area name is the first segment of the route. This makes me wonder if something like this ResourceController should be baked into portable areas itself.

I’m definitely interested in anyone has any opinions on it or have taken alternative approaches.

posted on Monday, April 5, 2010 4:52 PM Print
Comments
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Eric Hexter
4/6/2010 11:19 AM
I would like to see this handle case sensitivity. That is one of the ares that just is a pain with the embedded resources.

I an see adding the resource controller directly into mvccontrib, or as a seperate portable area that can be used to find the resource assembly by area name. hmm, need to think through that issue. anyway.. great job working through these issues,
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Steve
4/6/2010 1:31 PM
Yep, case sensitivity is an issue - the AssemblyResource class inside of MvcContrib already attempts to deal with the case sensitivity issue. Another reason I thought perhaps this should live inside MvcContrib - perhaps an implementation that they could both leverage to keep the code more DRY?
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Morten Mertner
7/5/2010 2:10 PM
How do the two approaches compare performance-wise?

My hunch would be that embedding the resources costs in performance and that it might be more useful with a startup task to export the resources to the file system.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
FunkMonkey33
11/7/2012 9:01 PM
Would this approach take care of image references inside of CSS style sheets?

I've been able to get my PA to serve up its embedded images, both within the PA, and to the outer web app. But the images referenced inside my CSS file (also an embedded resource) do not show up.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Steve Michelotti
11/8/2012 8:56 AM
@FunkMonkey33 - Yes it would work but you have to make sure that the path inside your CSS file is correct. Also pay attention as to whether you are using absolute or a relative path.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
FunkMonkey33
11/8/2012 7:31 PM
The CSS uses a relative path. I can get it working by changing

background-image: url(images/myPic.png);

To

background-image: url(../images/myPic.png);

But this offends my programming sensibilities. I'd much rather figure out a routing that allows me to keep my CSS the same (it's actually part of a 3rd party component) and route my images correctly anyway.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
FunkMonkey33
11/28/2012 6:53 PM
Can this be used to serve static htm files? I'm using jquery widgets and my html comes from a separate, standalone htm file at runtime.

Thanks!
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Steve Michelotti
11/29/2012 8:49 AM
@FunkMonkey33 - Yes, you could use this to serve static HTML files as well.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
FunkMonkey33
12/6/2012 7:40 PM
Hey Steve,

It looks like this functionality is now built into MvcContrib's portable areas, right? Just by judging the blog dates on http://geekswithblogs.net/michelotti/archive/2010/04/13/mvc-portable-areas-enhancement-ndash-embedded-resource-controller.aspx vs. this one.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Steve Michelotti
12/7/2012 9:45 AM
@FunkMonkey33 - Yes, I added this functionality to MvcContrib a while back.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
KJ
2/5/2014 10:19 AM
Embeded Resource files are not cached. So site takes more time. Is there any way to cache them? Thanks for your help in advance.
Gravatar
# re: MVC Portable Areas – Static Files as Embedded Resources
Steve Michelotti
2/7/2014 10:49 AM
@KJ - Well you could always just cache them in your own dictionary object. You'd have to tweak the code yourself of course.

Post Comment

Title *
Name *
Email
Comment *  
 

View Steve Michelotti's profile on LinkedIn

profile for Steve Michelotti at Stack Overflow, Q&A for professional and enthusiast programmers




Google My Blog

Tag Cloud