Instead of sock drawer approach, we could organize our code into modules
Here's how we wanted our code organization be like:
With modular approach, the controller and its views are grouped together. Though you will lose the IDE navigation aspect of controller<->view, you won't miss it much as your contoller and its views are near each other
Notice that we didn't include {controller} in the url, the controller and module are the same thing
And notice too that we hardcoded the controller to "_"
"_Controller" won't work, ASP.NET MVC always append Controller to the controller parameter. Hence "_" is enough
using System.Web.Mvc; namespace JustAspNetMvcThing.Areas.Hey { public class HeyAreaRegistration : AreaRegistration { public override string AreaName { get{ return "Hey"; } } public override void RegisterArea(AreaRegistrationContext context) { // Add this: context.MapRoute( name: "Hey_Jude_Dont_Make_SandwichModule_default", url: "Hey/Jude/Dont/Make/SandwichModule/{action}/{id}", defaults: new { action = "Index", id = UrlParameter.Optional, controller = "_" }, // instead of this string-based code: "JustAspNetMvcThing.App.Hey.Jude.Dont.Make.SandwichModule" parameter, we could use typeof: namespaces: new[] { typeof(JustAspNetMvcThing.App.Hey.Jude.Dont.Make.SandwichModule._Controller).Namespace } ); context.MapRoute( "Hey_default", "Hey/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } } }
using System.Web.Mvc; namespace JustAspNetMvcThing.Areas.Let { public class LetAreaRegistration : AreaRegistration { public override string AreaName { get { return "Let"; } } public override void RegisterArea(AreaRegistrationContext context) { // Add this: context.MapRoute( name: "Let_It_BeModule_default", url: "Let/It/BeModule/{action}/{id}", defaults: new { action = "Index", id = UrlParameter.Optional, controller = "_" }, namespaces: new[] { typeof(JustAspNetMvcThing.App.Let.It.BeModule._Controller).Namespace } ); context.MapRoute( "Let_default", "Let/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } } }
Here's the base controller for our modules:
using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace JustAspNetMvcThing.App { public class AppBaseController : Controller { public ViewResult View(object model, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") { return TheView(model, /*viewName*/ null, memberName); } public ViewResult View(string viewName = null, string masterName = null, [System.Runtime.CompilerServices.CallerMemberName] string memberName = "") { return TheView((object)null, viewName, memberName); } ViewResult TheView(object model, string viewName, string memberName) { // Skip(1) skips JustAspNetMVcThing // SkipLastN excludes the controller // Sample output: App/Hey/Jude/Dont/Make/SandwichModule string modulePath = string.Join("/", this.GetType().FullName.Split('.').Skip(1).SkipLastN(1)); // Sample output: /App/Hey/Jude/Dont/Make/SandwichModule/Bad.cshtml string viewFullPath = "/" + modulePath + "/" + memberName + ".cshtml"; return View(viewFullPath, model); } }// class AppBaseController static class Helper { // Because .Reverse() is bad: http://stackoverflow.com/questions/4166493/drop-the-last-item-with-linq#comment4498849_4166546 public static IEnumerable<T> SkipLastN<T>(this IEnumerable<T> source, int n) { var it = source.GetEnumerator(); bool hasRemainingItems = false; var cache = new Queue<T>(n + 1); do { if (hasRemainingItems = it.MoveNext()) { cache.Enqueue(it.Current); if (cache.Count > n) yield return cache.Dequeue(); } } while (hasRemainingItems); } }//class Helper }//namespace
The Sandwich module, notice the _Controller name? We do that, so the controller will always sort first in the folder
using JustAspNetMvcThing.Models; using System.Web.Mvc; namespace JustAspNetMvcThing.App.Hey.Jude.Dont.Make.SandwichModule { public class _Controller : AppBaseController { // GET: /Hey/Jude/Dont/Make/SandwichModule/Bad/1 public ViewResult Bad(int id = 0) { var p = new Person { FirstName = "Paul " + id }; return View(p); } } }
Here's one of the SandwichModule's views. Noticed that we can't use @model anymore. @model ModelHere is just a shorthand for @inherits System.Web.Mvc.WebViewPage<ModelHere>
@inherits System.Web.Mvc.WebViewPage<JustAspNetMvcThing.Models.Person> Hello @Model.FirstName
The Be module:
using System.Web.Mvc; namespace JustAspNetMvcThing.App.Let.It.BeModule { public class _Controller : AppBaseController { // GET: /Let/It/BeModule/ThePersonYouWantToBe public ActionResult ThePersonYouWantToBe() { return View(); } } }
Here's one of the BeModule's views:
@inherits System.Web.Mvc.WebViewPage I am me!
Happy Coding!