首先我用MVC4新增一個訂單查看的功能
1.創建控制器OrderController
namespace MvcApplication3.Controllers { public class OrderController : Controller { public ActionResult OrderView() { return View(); } } }
2.創建視圖 OrderView
@{ ViewBag.Title = "OrderView"; } <h2>OrderView</h2>
3.Global配置路由
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); routes.MapRoute( "OrderView", "OrderCenter/OrderView.html", new { controller = "Order", action = "OrderView" }, new string[] { "MvcApplication3" } ); }
我們在做MVC項目時,每次我們新增功能時,都要在golbal文件里注冊下該視圖的路由,當項目有10個,100個功能,那我們不配置死,而且都在一個文件里global里修改配置,維護起來非常費勁,有沒有好的辦法來解決這個問題尼,不用再修改global文件,就可自由的配置路由地址。
讓我們接下來一步步分析
首先我們看MVC路由類RouteCollection的擴展方法 MapRoute 的參數屬性
// // 摘要: // Maps the specified URL route and sets default route values and namespaces. // // 參數: // routes: // A collection of routes for the application. // // name: // The name of the route to map. // // url: // The URL pattern for the route. // // defaults: // An object that contains default route values. // // namespaces: // A set of namespaces for the application. // // 返回結果: // A reference to the mapped route. // // 異常: // System.ArgumentNullException: // The routes or url parameter is null. public static Route MapRoute(this RouteCollection routes, string name, string url, object defaults, string[] namespaces);
再看下我們在Golbal里調用此方法
routes.MapRoute( "OrderView", --對應name "OrderCenter/OrderView.html", --對應url new { controller = "Order", action = "OrderView" }, --對應object new string[] { "MvcApplication3" } --對應namespaces );
看第三個參數 defaults 它包含了路由的Controller名稱,Action名稱,第四個參數namespaces表示這個Controller所在的命名空間,我們看看我們之前新建的控制器OrderController它的類型是class,而它下面的Action是一個返回類型是ActionResult的方法,再想到命名空間,由此我們是否可以利用反射技術來來循環讀取當前應用程序下所有Controller,每個Controller的所有方法Action以及每個Controller所在命名空間
下面我們寫下面一段偽代碼來分析我們的思路:
var 控制器類集合=讀取當前所有控制器類(); foreach (var 控制器 in 控制器類集合) { var action方法數組=獲取控制器所有Action方法(); var namespance=獲取當前控制器所在命名空間; var controllerName=獲取控制器名稱(); foreach(var action in action方法數組) { var actionName=獲取action名稱(); var routerUrl="OrderCenter/OrderView.html"; //注冊 routes.MapRoute( actionName, routerUrl, new { controller =controllerName, action = actionName }, new string[] { namespance } ); } }
這段偽代碼算是解決我們今天講的主題問題的一部分,為什么尼,因為第二個參數url 我們無法識別或知道每個Controller路由的地址,這樣問題還沒有解決,我們繼續苦逼的維護這我們的golbal路由文件,新增一個功能,來加個配置,如果每個Controller有個屬性URL,我們可以設置這個屬性,那問題不就解決了嘛,那如何給Controller添加屬性尼,這里我們可以利用到C# 特性 Attribute
關於特性MSDN給的定義:
特性提供功能強大的方法,用以將元數據或聲明信息與代碼(程序集、類型、方法、屬性等)相關聯。特性與程序實體關聯后,即可在運行時使用名為“反射”的技術查詢特性。
特性具有以下屬性:
-
特性可向程序中添加元數據。元數據是有關在程序中定義的類型的信息。所有的 .NET 程序集都包含指定的一組元數據,這些元數據描述在程序集中定義的類型和類型成員。可以添加自定義特性,以指定所需的任何附加信息。。
-
可以將一個或多個特性應用到整個程序集、模塊或較小的程序元素(如類和屬性)。
-
特性可以與方法和屬性相同的方式接受參數。
-
程序可以使用反射檢查自己的元數據或其他程序內的元數據。有關更多信息。
所以我們定義一個特性類
[AttributeUsage(AttributeTargets.All, Inherited = true)] public class RouteAddressAttribute : Attribute { public RouteAddressAttribute() { } public RouteAddressAttribute(string name, string address) { this.Name = name; this.Address = address; } /// <summary> /// 地址 【正是我們想要的URL】 /// </summary> public string Address { get; set; } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } }
定義了特性類,看我們怎么用它,此時我們再看我的Controller
namespace MvcApplication3.Controllers { public class OrderController : Controller { [RouteAddress(Name = "訂單查看", Address = "OrderCenter/OrderView.html")] public ActionResult OrderView() { return View(); } } }
再把我們之前的偽代碼編程成真實代碼,代碼實現如下
namespace MvcApplication3 { public class RouteMap { public static void Redirection(RouteCollection routes, string assemblyName) { Assembly assembly = Assembly.Load(assemblyName); Type[] types = assembly.GetTypes(); List<string> ListAdress = new List<string>(); foreach (Type type in types) { #region 讀取所有Controller if (type.Name.Contains("Controller")) { string nameSpace = type.Namespace; string controller = type.Name.Replace("Controller", ""); string action = ""; string address = ""; string routeName = ""; MemberInfo[] memberInfos = type.GetMethods(BindingFlags.DeclaredOnly | BindingFlags.Instance | BindingFlags.Public); #region 讀取每個Controller的Action foreach (var item in memberInfos) { action = item.Name; routeName = type.Name + action; var objAttr = item.GetCustomAttributes(typeof(RouteAddressAttribute), false); if (objAttr != null && objAttr.Length > 0) { #region 獲取特性RouteAdress RouteAddressAttribute temp = (RouteAddressAttribute)objAttr.First(); if (temp != null) { address = temp.Address; if (!ListAdress.Contains(address)) { //實現注冊 routes.MapRoute(routeName, address, new { controller = controller, action = action }, new string[] { nameSpace }); ListAdress.Add(address); } else throw new Exception("存在相同路由地址:" + address); } #endregion } else { #region 沒加特性的則顯示默認地址 Controller/Action address = string.Format("{0}/{1}", controller, action); if (!ListAdress.Contains(address)) { //實現注冊 routes.MapRoute(routeName, address, new { controller = controller, action = action }, new string[] { nameSpace }); ListAdress.Add(address); } else throw new Exception("存在相同路由地址:" + address); #endregion } } #endregion } #endregion } } } }
這樣Golbal文件里我們添加一行代碼就行了。
public static void RegisterRoutes(RouteCollection routes) { routes.IgnoreRoute("{resource}.axd/{*pathInfo}"); //調用 RouteMap.Redirection(routes, "MvcApplication3"); }
綜上所述,利用特性,利用反射,解決了開發人員繁瑣的路由配置工作。
關於特性和反射技術,大家可以看下MSDN,本文不做詳細介紹。