Multi-Project (Portable) Areas Walkthrough

With MVC2, the MVC team introduced Areas, a way to decompose and organize a large MVC application into smaller sub-applications.  The guys who work on MvcContrib built on top of this the concept of Portable Areas.

The Asp.NET site has an intro video on Areas here.

Eric Hexter gives a good overview of portable areas here, and he talks about it with Jeffrey Palermo on the Polymorphic Podcast here.

Eric and Jeffery wrote pretty thorough walkthroughs of portable areas, but they’ve since been outdated by some API changes in MvcContrib.  When a teammate and I attempted to create and consume our own portable area from scratch, we were thwarted by this, and the fact the the above examples also walk through other, unrelated MVC/MvcContrib features.  It took us about a day-and-a-half of trial-and-error to get our own from-scratch portable area, and the necessary steps turned out to be relatively simple, so I thought I’d capture those here.

Note: The MvcContrib project has a Portable Areas Sample in the codebase, at http://mvccontrib.codeplex.com/.  As of this post, the sample works perfectly, but we needed to create our own, from scratch for a new project.  There also appears to be a portable area project template in the works, too, which would be helpful.

Caveat:

I used MVCContrib 2.0.47.0(unreleased) to build this example.  There were some API changes from the latest MvcContrib release (2.0.36.0, 4/14/2010).  I pushed the result to http://github.com/pjboudrx/PortableAreasWalkthrough

Overview/Checklist

  1. Build The Area (Models, Views & Controllers)
  2. Portable Area Registration
  3. Embedded Views and Static Content
  4. “Areas” folder with a Web.Config

2 Projects

To develop the portable area, we found it best to have a related ‘host’ MVC project in the solution, in addition to the portable area project itself.  This gives us an environment to test our portable area.

‘Parent’ Project: MVC2 Web Application

Add a reference to MVCContrib.

‘Portable’ Project: Class Library

Add references to MVCContrib, System.Web.Mvc and System.Web.Routing

Build the portable area

This is equivalent to taking a single area and moving under the root of the project.  You have a folder with the area name.  In that folder: Controllers, Models, Views folders.  For example, my portable area is named ‘CoolComponent’:

Portable Area Registration

The portable area registration class must reside in the namespace <RootNamespace>.<AreaName>; for example: “PortableArea.CoolComponent” Example

namespace PortableArea.CoolComponent
{
	public class CoolComponentRegistration : PortableAreaRegistration
	{
		public override string AreaName
		{
			get { return "CoolComponent"; }
		}

		public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
		{
			base.RegisterArea(context, bus);
			context.MapRoute(
 				"CoolComponent_Default",
 				AreaName + "/{controller}/{action}/{id}",
 				new {controller = "HelloWorld", action = "Index", id = UrlParameter.Optional });
 		}
 	}
 }

What’s going on here?  First, the simple stuff.  The “AreaName” property just fills in a spot in the template pattern established by PortableAreaRegistration for use elsewhere.  The call to “context.MapRoute()” is the same as a call to “routes.MapRoute()”; only this time, the AreaName is fixed in the URL pattern.  Standard MVC Area stuff.

The line with all the juice is the call to “base.RegisterArea().”  The base method does several things for us:

  1. Sends a “PortableAreaStartupMessage” on the Application Bus
  2. Registers routes and a controller for embedded Images, Styles, and Scripts, to make the Area fully portable
  3. Registers the Embedded View Engine, so your embedded .aspx view templates can be found at runtime

Add controller(s) and view(s)

Because your portable area is just a class library, you may not see VS templates like you’d expect.
Note: views must be embedded resources.

Consume the Portable Area

Make sure you have an “Areas” folder in the Parent project, and copy Web.config from “Views” to “Areas” if it does not already exist.


Add a reference to the Portable Area project
Global.asax.cs: Add calls to ‘RegisterAllAreas’ and ‘RegisterEmbeddedViewEngine’ (Example)

protected void Application_Start()
{
	// This registers areas, both in-project and portable
	AreaRegistration.RegisterAllAreas();

	// This ensures the views embedded in the Portable Area can be found
	PortableAreaRegistration.RegisterEmbeddedViewEngine();

	// Typical MVC route registration call
	RegisterRoutes(RouteTable.Routes);
}

Test it!

Browse to /CoolComponent/HelloWorld/Index

Area-aware links in the Parent Project

If you want to link (or RenderAction) to the portable areas routes, you will need to ensure ‘area’ is in the route data.

Example

<!-- Ensure 'area' is in the route data  -->
<%=Html.ActionLink("Use the Cool Component", "Index", "HelloWorld", new {area="CoolComponent"}, null) %>
<!-- Ensure 'area' is in the route data  -->    <%=Html.ActionLink("Use the Cool Component", "Index", "HelloWorld", new {area="CoolComponent"}, null) %>

Portable Images, Scripts and Styles

(Thanks to Steve Harman for this additional insight.)

To make a fully self-contained portable area, static content such as css, images and javascript needs to be embedded in the assembly, and found in a special way, just as the view templates are.  To do this:

  1. be sure to call “base.RegisterArea()” in your Portable Area Registration
  2. Include the static files as embedded resource in the conventional location: