Tag Archives: ASP.NET MVC

Single-Project Areas in ASP.NET MVC 2

The ASP.NET MVC framework brought a lot of benefits to the Microsoft web developer, such as easier automated testing and better separation of concerns, but it did come with its share of pain points.  One of those pain points was the difficulty of organizing the files in your web project.  By default, all folders that contained views, which were named after the controllers in your application, had to be directly under the “Views” folder that was provided by the framework.  For large applications, the number of folders underneath “Views” could get quite unwieldy.

To help alleviate this, in the first preview of version 2 of the MVC framework, the concept of “Areas” was introduced to allow an additional hierarchical level to controller and view organization.  It was implemented in Preview 1 using separate web projects for each area, each with its own “Controllers” and “Views” folder.

This was a definite improvement, but there was some pretty quick feedback from the community about the implementation.  Having a separate project for each area means that large solutions would end up with quite a few projects, which can dramatically impact compilation time.  I can speak from experience; the main solution I work on had over 80 projects in it when I first joined my current team.  Build time  was usually about 10 minutes, and that was just compilation, no tests or other things going on in the build.  When we reduced it to three projects, build time went down to about 10 seconds.  Needless to say, as our team starts thinking about doing some MVC work, we don’t want to go back to that place.

Thankfully, in preview 2, the MVC team provided the ability to create all your areas within a single web project.  This provides all the organizational benefits without the impact to compilation time.  To add areas to your MVC web project, follow these steps:

  1. Add a folder named “Areas” to your web project.
  2. Add a folder underneath the Areas folder with the name of the area you want to create, “Accounting” for example.
  3. Add a folder called “Controllers” under the Accounting folder.  Now, when you right-click on this folder, you’ll get the “Add Controller” context menu option.
  4. Add a folder called “Views” under the Accounting folder.  This will work just like the Views folder that gets created as part of the MVC project template.  You’ll have one folder inside the Views folder for each controller in your area.
  5. Add a new class  file to the Accounting folder named “Routes.cs”.  This class will need to inherit from AreaRegistration and override the AreaName property.  It should end up looking something like this:
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Web;
    using System.Web.Mvc;
    
    namespace MyProject.Areas.Accounting
    {
        public class Routes : AreaRegistration
        {
            public override string AreaName
            {
                get { return "accounting"; }
            }
    
            public override void RegisterArea(AreaRegistrationContext context)
            {
                context.MapRoute(
                    "accounting_default",
                    "accounting/{controller}/{action}/{id}",
                    new { controller = "Invoices", action = "ShowUnpaid", id = "" }
                );
            }
        }
    }
  6. You’ll also need to add a line to your Global.asax.cs file.  Simply call “AreaRegistration.RegisterAllAreas();” just before the first call to routes.MapRoute().

That’s it!  Well, almost.  Since you can have more than one area with the same controller name, when you create an ActionLink or something similar, you have to specify which area you intend to link to.  For instance, if you wanted to link to the ShowUnpaid action of the Invoices controller in the Accounting area from some other area, you’d do it like so:

    <%= Html.ActionLink("Unpaid Invoices", "ShowUnpaid", "Invoices", new {area = "accounting"}, null) %>

Note that if you’re linking to a controller from a view within the same area, you don’t have to specify it in the ActionLink call.

I think this is a great feature, and should allow us to maintain the current level of logical partitioning within our application.  Thanks to the MVC team for putting this in!

ASP.NET MVC Reusability Revisited

A few weeks ago, I wrote a post about the lack of a mechanism to seperate a unit of both logic and presentation (like a UserControl in WebForms) in ASP.NET MVC.� Well, it turns out that Rob Conery had actually come up with something called a ComponentController that took care of that, and used it in his MVC Storefront application.� (Note to self: need to catch up on those videos he’s making.)�

It actually took the removal of that feature in Preview 4 to alert me that it existed.� Not to fear, though, it’s been replaced by something called RenderAction, which will allow you to call a regular old controller action from a view like so:

<% Html.RenderAction(x=>x.MyControllerAction()) %>

Isn’t that awesome?  I sure think so.  Be warned, though, this particular feature may be even more likely to be changed than the rest of the MVC framework.  It’s part of a separate assembly called Microsoft.Web.Mvc.dll.  Stuff in there won’t be in the initial RTM, but in a future release of the framework.

Phil Haack has some reservations about this violating the purity of the MVC pattern, so I think the likelihood that this will change is close to 100%.  They’re working on some other ideas, though, so hopefully the MVC team will come up with some magic that better adheres to a mindset of separation of concerns.

For the moment, though, I’m happy!  :-)

Where’s the Real Reuse in ASP.NET MVC?

Just a quick note here.  I was playing around with ASP.NET MVC today, and I got to thinking, how does one encapsulate both behavior and markup (a la UserControls in WebForms) when using MVC?  Well, there’s a thing called a ViewUserControl that you can use in your ViewPages, but all that really does is encapsulate the markup and rendering.  You’d use them like this:

MyViewUserControl.ascx

And then you would call it from the ViewPage:

MyViewPage.aspx

If you use a typed ViewUserControl, you can feed the object to the RenderUserControl method as an additional parameter rather than going to the ViewData collection, but that’s not what gets to me.

The controller that renders MyViewPage.aspx is still responsible for retrieving all the data that MyViewUserControl.ascx uses. So, everywhere that I want to display that list of sponsors, I have to remember to write the code to go get it. That’s not really reuse, is it?

There was a comment on Rob Conery’s blog back in January that I ran across that suggested something like this:

That way, there can be one controller that’s responsible for pulling that Sponsor data from the data access layer. I’m not sure if this is possible; I’m not familiar enough with the way MVC works to know if a second (or third, or nth) controller can be created and called once the main controller has started rendering MyViewPage. From an end-user perspective, something like that would be great, since it would let us truly encapsulate every aspect of what that ViewUserControl is about. Here’s hoping!