However, I find the code organization of that approach is not symmetrical enough, I wanted the code organization of controller and views be mirror image of each other, like this:
Here's the supporting code:
using System.Collections.Generic; using System.Linq; using System.Web.Mvc; namespace JustAspNetMvcThing.App { public class AreaBaseController : 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(/*model*/ (object)null, viewName, memberName); } ViewResult TheView(object model, string viewName, string memberName) { if (viewName != null && model == null) return View(viewName, model: null); string origName = this.GetType().Name; // "Controller" string is stripped from "ItController" // "Controller" is stringly-typed, there's a better way, just wanted to make a short code // Sample output: It string controllerName = origName.Substring(0, origName.Length - "Controller".Length); // Skip(3) skips JustAspNetMVcThing.App.Controllers // SkipLastN excludes the controller, example: Hey/Jude/Dont/Make // Sample output: Hey/Jude/Dont/Make/It string controllerPath = string.Join("/", this.GetType().FullName.Split('.').Skip(3).SkipLastN(1)) + "/" + controllerName; // Sample output: /App/Views/Hey/Jude/Dont/Make/It/Bad.cshtml string viewFullPath = "/App/Views/" + controllerPath + "/" + memberName + ".cshtml"; return View(viewFullPath, model); } } 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); } } }
Then do this on the Area registration:
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_default", url: "Hey/Jude/Dont/Make/{controller}/{action}/{id}", defaults: new { action = "Index", id = UrlParameter.Optional }, namespaces: new[] { "JustAspNetMvcThing.App.Controllers.Hey.Jude.Dont.Make" } ); context.MapRoute( "Hey_default", "Hey/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } } }
Voila! Your controller can continue its normal business. The View(object) resolves to View(object,memberName="") hence we are able to hook the normal View signature from the controller that inherits from AreaBaseController
using JustAspNetMvcThing.Models; using System.Web.Mvc; namespace JustAspNetMvcThing.App.Controllers.Hey.Jude.Dont.Make { public class ItController : AreaBaseController { // GET: /Hey/Jude/Dont/Make/It/Bad/1 public ViewResult Bad(int id = 0) { var p = new Person { FirstName = "Paul " + id }; return View(p); } } }
Lastly, we must use @inherits instead of @model on our views. @model is a shorthand for @inherits
@inherits System.Web.Mvc.WebViewPage<JustAspNetMvcThing.Models.Person> Hello @Model.FirstName
Happy Coding!
No comments:
Post a Comment