淘小店

RouteData.GetRequiredString("key")与RouteData.DataTokens["key"]

今天遇到一个模板页记录路由信息的问题,MVC页面可以通过

<%=ViewContext.RouteData.GetRequiredString("controller") %>/<%=ViewContext.RouteData.GetRequiredString("action") %>

获得页面的路由信息,<%=ViewContext.RouteData.GetRequiredString("key") %>如果key没有在路由配置中,这段代码会报错(不会返回null的)。

网上有些人写的通过 RouteData.DataTokens["key"] 获得参数,原文:“所以如果当你无法确定某个参数(通常是QueryString)是否存在时,最好选用RouteData.DataTokens["key"]的放法获取,除非你确认这个key必然存在,且期望类型就是string。

 DataTokens返回的是一个namespace的集合,为的是区分同名的controller,当然建议大家最好每个controller 命名都是唯一的,

以下引用网上找的《深入分析 ASP.NET Mvc 1.0 – 1. 深入MvcHandler》 一文中的解释:

在程序中不同的namespace下面可能会存在同名的controller,所以这里用namespace区分这些同名的但不同意义的controller。namespace可以在Global.asax.cs的RegisterRoutes(…)方法中指定,比如:

routes.MapRoute(                 "Default",                                              // Route name                 "{controller}/{action}/{id}",                           // URL with parameters                 new { controller = "Home", action = "Index", id = "" },  // Parameter defaults                 new { httpMethod = new HttpMethodConstraint("get", "post") }                 ,new string[]{"Namespace1"}             );

 

在继续看GetControllerType(…)方法,在GetControllerType(string controllerName) 方法中最终都是通过调用GetControllerTypeWithinNamespaces(…)方法返回controller type的, 具体代码如下:

private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces)         {             // Once the master list of controllers has been created we can quickly index into it             ControllerTypeCache.EnsureInitialized(BuildManager);              IList<Type> matchingTypes = ControllerTypeCache.GetControllerTypes(controllerName, namespaces);             switch (matchingTypes.Count)             {                 case 0:                     // no matching types                     return null;                  case 1:                     // single matching type                     return matchingTypes[0];                  default:                     // multiple matching types                     // we need to generate an exception containing all the controller types                     StringBuilder sb = new StringBuilder();                     foreach (Type matchedType in matchingTypes)                     {                         sb.AppendLine();                         sb.Append(matchedType.FullName);                     }                     throw new InvalidOperationException(                         String.Format(                             CultureInfo.CurrentUICulture,                             MvcResources.DefaultControllerFactory_ControllerNameAmbiguous,                             controllerName, sb));             }         }

首先将执行ControllerTypeCache.EnsureInitialized(BuildManager);  他的作用是将程序中所有assembly中所有以Controller结尾的类放在缓存中,看一下

EnsureInitialized(…)方法的代码:

ControllerBuilder.cs

public void EnsureInitialized(IBuildManager buildManager) { if (_cache == null) { lock (_lockObj) { if (_cache == null) { List<Type> controllerTypes = GetAllControllerTypes(buildManager); var groupedByName = controllerTypes.GroupBy( t => t.Name.Substring(0, t.Name.Length - "Controller".Length), StringComparer.OrdinalIgnoreCase); _cache = groupedByName.ToDictionary( g => g.Key, g => g.ToLookup(t => t.Namespace ?? String.Empty, StringComparer.OrdinalIgnoreCase), StringComparer.OrdinalIgnoreCase); } } } }

这是一个具有2级结构的缓存, 以controll name为key, 以Lookup<string, Type>对象为值保存到缓存中,而Lookup<string, Type>的结构是以namespace为key, 以

controller type为值的键值集合,这个2级结构的作用就是上面提到的用来解决不同namespace中同名controller的问题。

 

GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces) 方法中:

ControllerTypeCache.GetControllerTypes(controllerName, namespaces) 就是去找具有相同controllerName的controller type,不过这里有个问题就是如果没有在

Global中或其它地方提供默认的namespace而不同namespace下存在同名的controller,就导致GetControllerTypes(…)方法返回的controller数量大于1,这时程序会在

switch语句处抛出一个异常,所里一定要注意,尽量不要在不同的namespace中定义同名的controller

当找到一个对应的 controller type后,就将这个type返回给上面的CreateController(RequestContextrequestContext, string controllerName) 方法中调用

GetControllerType(controllerName); 方法的地方, 然后再调用GetControllerInstance(controllerType); 方法将方法反射成具体的controller类并返回到ProcessRequest(…)中,并依次执行controller.Execute(RequestContext) –> factory.ReleaseController(controller);   至此整个MvcHandler的流程执行完毕.

 

附上MvcHandler的时序图:

” 

 

 

posted on 2011-05-09 11:41  adu123  阅读(1089)  评论(0编辑  收藏  举报

导航

淘小店