Ad

What's The Best Way To Change How Areas Get Created In Razor MVC?

- 1 answer

I am transitioning to using Areas in a project, and each time I make a new one I have to change a few things to make it fit in with how I'd like things to work. The changes involve:

  • Removing one of the folders that gets created
  • Modifying the web.config file slightly
  • Adding a new file or two with some defaults
  • Modifying the AreaRegistration file slightly

I would rather not have to do these same things every time I create a new area because I'm afraid that the convention will be forgotten and something will get messed up. Is there a way to modify the defaults for New > Area? Or perhaps would it be possible to make a new file template that does what I'd like?

Ad

Answer

I am transitioning to using Areas in a project

I have been doing something similar for a few months now, but starting with an old WebForms project, adding MVC components via VS 2013's Add -> New Scaffolded Item... feature, in order to leverage MVC's structure and routing, on the server side. I have also been incorporating WebAPI and SignalR on the server side. I am definitely still a beginner, but being maybe a little further along in a similar process, here some of my thoughts and questions about what you want to achieve:


  • Removing one of the folders that gets created
  • Modifying the web.config file slightly
  • Adding a new file or two with some defaults

What environment are you developing in? For instance, if you're using VS 2013's scaffolding you can most likely modify the T4 templates to prevent or redirect the creation of folders, classes, and such. Here is a post addressing where you might find those templates, and here is just one of many intros out there for T4. The path to the templates on your machine will vary based on the VS version. By modifying or creating new templates you should be able to accomplish all 3 points above relatively easy. If you are working in such an environment I will try to expound on exactly how you might go about modifying the out of the box templates (there are probably already plenty of posts that you could refer to which would do a better job though).


  • Modifying the AreaRegistration file slightly

If you are working with MVC 5, or later, I would recommend using attribute routing. This seems more standard when working within the API paradigm (inheritting from ApiController instead of Controller), but I have found extending this to traditional MVC controllers to be very useful.

Assuming MVC 5, I would recommend modifying AppStart\RouteConfig.cs's RegisterRoutes() method, to enable attribute routing and then add a catch-all route such as:

routes.MapMvcAttributeRoutes();

routes.MapRoute(
    "NotFound",
    "{*url}",
    new {
        controller = "Error",
        action = "Index"
    });

At that point I would delete the xAreaRegistration.cs files, and remove the call to RegisterAllAreas() from Global.asax, but you don't have to go that far. Then I decorate my controllers' classes (wherever they live) and their methods, with attributes as needed. Using attribute routing I've been able to free myself from the traditional MVC conventions. I can still fall back to them, but I can then place my controllers anywhere in my project, and easily define their actions' routes.

That being said, you should probably follow some organizational standards for groups of similar routes, so that as the project grows you don't find yourself wasting a lot of time searching for all controllers/actions matching some set of routes.


Here's a simple example of how this actually translates from URI into method call:

If I had Controller ctl with action act, logically in Area ar, the URI being http://localhost/ar/ctl/a/1, would be routed to MyControllerNameDoesNotMatter.Index(1), below, by using attribute decoration such as:

[RouteArea("ar")]
[RoutePrefix("ctl")]
public class MyControllerNameDoesNotMatter : Controller {
    ...        
    [Route("a/{optionalParamDefaultsToNegativeOne=-1}")]
    public ActionResult Index(int optionalParamDefaultsToNegativeOne) {
        ...
    }
}

This way of looking at Area is more logical than physical, in that the MyControllerNameDoesNotMatter.cs file does not have to be in the Area\ar\Controllers folder. Since adopting this I have actually drifted away from using the Area scaffolding, other than to lay out application modules at a very high level - think sub-application that could be a stand alone SPA.

I have tried to make the "catch-all" route attribute based, and remove it from RouteConfig.cs, but I have not been able to do this successfully. The issue is route precedence. It works fine as a catch-all for invalid routes. But for a valid route, multiple (2) routes end up being matched because the two attribute routes have the same order of precedence. Whereas, with the catch-all defined in RouteConfig.cs, after registering all attribute routes, the attribute route takes precedence. I have found mixed answers that attempt to address this issue. There seems to have been a Number or Order parameter for the Route attribute at some point, but I have had no luck with that. This seems to be an unresolved issue. Just one of many SO questions, which have gone unanswered.

Edited to account for mvc4 tag

I had not seen the mvc4 tag prior to posting. There are nuget packages available that will achieve much of the same functionality, such as scaffolding and attribute routing for earlier versions of MVC.

Ad
source: stackoverflow.com
Ad