MVC Portable Areas
Introduction:
An MVC Portable Area is really just a dll that contains the views, controllers, scripts, etc… needed to use in a website that is either a Web Forms website or an MVC website. I will cover the MVC website here.
Why Use One?
I have used portable areas in many projects since they came out. A developer can use them for a reusable widget or a complete engine. I have actually used them for both.
Set Up:
I am going to assume that you have a new project. It is just as easy to add a portable area to an existing solution.
First, let’s create a blank solution. Click on New Project, then select Other Project Types –> Visual Studio Solutions. Name it PortableAreaDemo
Next, right click on the solution in Solution Explorer and Add New Project. Choose Web and ASP.NET MVC 3 Application. I Named mine PortableAreaDemo.Mvc.
Choose Internet Application on the next screen:
Next, add another project. This is also going to be a ASP.NET MVC 3 Web Application. I called mine PortableAreaDemo.PortableAreas. Note, you might get an error here or might not see your solution node. If so, follow this link: http://stackoverflow.com/questions/7457935/solution-folder-not-showing-in-visual-studio-2010-how-can-i-make-it-visible
This time, just make it an empty application.
Hopefully you see something like this:
Next, go ahead and delete the Controllers, Views, Models, Scripts, and Content folders under the PortableAreas project. Also, remove the Global.asax inside that project as well.
Now, right click on the PortableAreas project and add an Area. Let’s call it Demo. This should produce something like this:
Notice now that it created an AreaRegistration child called DemoAreaRegistration. We will need to now go and add a Library Package Reference to this. Right click on References under the PortableAreas project –> Add Library Package Reference. Then click on Online –> All. Wait for it to load, then type MvcContrib in the search box. Install the package.
Now go back into DemoAreaRegistration.cs and change the parent name to PortableAreaRegistration.
Move the routes out into a private method called RegisterRoutes. By default, if you were to call the base.RegisterAreas here, it would set up the EmbeddedResource. However, your routes get set up in the wrong order and you will lose control once your portable area gets more sophisticated. I would strongly suggest not using the built in ones here and registering them yourself. Override the RegisterArea that passes the IApplicationBus instead of the one that just has the context:
1: public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
2: {
3: RegisterRoutes(context);
4: RegisterAreaEmbeddedResources();
5: }
Then add a private method called RegisterRoutes and add the following routes into it:
1: private void RegisterRoutes(AreaRegistrationContext context)
2: {
3: context.MapRoute(
4: AreaName + "_scripts",
5: base.AreaRoutePrefix + "/Scripts/{resourceName}",
6: new { controller = "EmbeddedResource", action = "Index", resourcePath = "scripts" },
7: new[] { "MvcContrib.PortableAreas" }
8: );
9:
10: context.MapRoute(
11: AreaName + "_images",
12: base.AreaRoutePrefix + "/images/{resourceName}",
13: new { controller = "EmbeddedResource", action = "Index", resourcePath = "images" },
14: new[] { "MvcContrib.PortableAreas" }
15: );
16:
17: context.MapRoute(
18: AreaName + "_default",
19: base.AreaRoutePrefix + "/{controller}/{action}/{id}",
20: new { action = "Index", id = UrlParameter.Optional },
21: new[] { "PortableAreaDemo.PortableAreas.Areas.Demo.Controllers", "MvcContrib" }
22: );
23: }
When all is done, your registration class should look like this:
1: using System.Web.Mvc;
2: using MvcContrib.PortableAreas;
3:
4: namespace PortableAreaDemo.PortableAreas.Areas.Demo
5: {
6: public class DemoAreaRegistration : PortableAreaRegistration
7: {
8: public override string AreaName
9: {
10: get
11: {
12: return "Demo";
13: }
14: }
15:
16: public override void RegisterArea(AreaRegistrationContext context, IApplicationBus bus)
17: {
18: RegisterRoutes(context);
19: RegisterAreaEmbeddedResources();
20: }
21:
22: private void RegisterRoutes(AreaRegistrationContext context)
23: {
24: context.MapRoute(
25: AreaName + "_scripts",
26: base.AreaRoutePrefix + "/Scripts/{resourceName}",
27: new { controller = "EmbeddedResource", action = "Index", resourcePath = "scripts" },
28: new[] { "MvcContrib.PortableAreas" }
29: );
30:
31: context.MapRoute(
32: AreaName + "_images",
33: base.AreaRoutePrefix + "/images/{resourceName}",
34: new { controller = "EmbeddedResource", action = "Index", resourcePath = "images" },
35: new[] { "MvcContrib.PortableAreas" }
36: );
37:
38: context.MapRoute(
39: AreaName + "_default",
40: base.AreaRoutePrefix + "/{controller}/{action}/{id}",
41: new { action = "Index", id = UrlParameter.Optional },
42: new[] { "PortableAreaDemo.PortableAreas.Areas.Demo.Controllers", "MvcContrib" }
43: );
44: }
45:
46: }
47: }
So a little explanation of this. When your application first starts, there is a little piece of code in Application_Start that will end up calling this Portable Area Registration. Area routes should always be called before your normal routes to give it a chance to find the Area name. Otherwise, the default route would think the Area name was a controller name. Here is the piece of code that calls the portable area registration:
1: protected void Application_Start()
2: {
3: AreaRegistration.RegisterAllAreas();
4: ...
Ok, so now let’s create a controller in the Demo area. Let’s call it “World”. Now let’s add an action to it. Can you guess the name? Yep, that’s right “Hello”. Create a Razor partial View for it. Type “Hello World” in the view. When all is said and done, you should look something like this:
Now, here is a very IMPORTANT step. Anything that is either a view, css, javascript, image, etc must be an embedded resource. What does this mean? It means it will be added to the dll instead of being found on the file system. This is important because a portable area travels with the dll and not with the project itself. Yes, it will cause you to have to build every time you want to see a change in the page or js. This is why you must decide up front if it is worth the development time that it will take. For me, the projects that I have used them for, it has been. We wanted something reusable that we could move from solution to solution without having to rewrite.
So in order to make it embedded, right click on the Hello.cshtml file and choose Properties. Change the Build Action to Embedded Resource.
Next, add the reference for the PortableAreas Project to the main Mvc project.
Now add a folder called Areas under the Mvc project. Open the folder Views under the Mvc project and copy the Web.config up to the Areas folder that you just created.
You should now look like this:
So, why are we adding the folder called Areas and a web.config into it? It is because when the dll gets put into the project, it will put your portable area into this folder underneath the covers. This is where it pretends it is and will look for the views and such inside of this folder.
Now, in the Index.cshtml view under Home, add the line at the bottom:
1: @Html.Action("Hello", "World", new { area = "Demo"})
Set the PortableAreaDemo.Mvc as the Startup Project and Press “Start Debugging”, you should now see:
Hopefully you got it working. If not, go back through and check the steps, otherwise, you can comment here or catch me on twitter at @mike_d_moser and I can try and help you out. Thanks and happy coding.


