asp.net mvc(五)
我们在创建一个Controller时都会默认遵守这样的规定,名称+“Controller”,但我们在页面访问这个Controller时并不会写上后面的Controller字符,为什么系统能自动识别呢?这里我们也可以从Controller源码中得知。
第一:Controller中的重要方法:CreateController,由它负责创建具体的Controller。这个方法根据用户传的controllerName(例如:Home)来取得实际的Controller的类型,其中用到这样的方法:this.GetControllerType(controllerName)。
{
if (requestContext == null)
{
throw new ArgumentNullException("requestContext");
}
if (string.IsNullOrEmpty(controllerName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
this.RequestContext = requestContext;
Type controllerType = this.GetControllerType(controllerName);
return this.GetControllerInstance(controllerType);
}
第二:GetControllerType方法体。主要是调用GetControllerTypeWithinNamespaces方法,根据controllerName和controller所对应的命名空间来取得实际Controller的类型。如果RequestContext.RouteData.DataTokens包含了Namespaces,则根据传递信息中的Namespaces和controllerName来取,否则按默认的命名空间和controllerName来取。
{
object obj2;
Type controllerTypeWithinNamespaces;
if (string.IsNullOrEmpty(controllerName))
{
throw new ArgumentException(MvcResources.Common_NullOrEmpty, "controllerName");
}
if ((this.RequestContext != null) && this.RequestContext.RouteData.DataTokens.TryGetValue("Namespaces", out obj2))
{
IEnumerable<string> collection = obj2 as IEnumerable<string>;
if (collection != null)
{
HashSet<string> set = new HashSet<string>(collection, StringComparer.OrdinalIgnoreCase);
controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(controllerName, set);
if (controllerTypeWithinNamespaces != null)
{
return controllerTypeWithinNamespaces;
}
}
}
HashSet<string> namespaces = new HashSet<string>(this.ControllerBuilder.DefaultNamespaces, StringComparer.OrdinalIgnoreCase);
controllerTypeWithinNamespaces = this.GetControllerTypeWithinNamespaces(controllerName, namespaces);
if (controllerTypeWithinNamespaces != null)
{
return controllerTypeWithinNamespaces;
}
return this.GetControllerTypeWithinNamespaces(controllerName, null);
}
第三:接下来我们来看上面代码段用到的GetControllerTypeWithinNamespaces方法。这个方法用到了ControllerTypeCache,将在下面分析。可以看出,这个方法只返回所有符合条件的Type中的第一个类型,如果没有取到则返回空,如果查找到多个则返回第一个类型。
{
this.ControllerTypeCache.EnsureInitialized(this.BuildManager);
IList<Type> controllerTypes = this.ControllerTypeCache.GetControllerTypes(controllerName, namespaces);
switch (controllerTypes.Count)
{
case 0:
return null;
case 1:
return controllerTypes[0];
}
StringBuilder builder = new StringBuilder();
foreach (Type type in controllerTypes)
{
builder.AppendLine();
builder.Append(type.FullName);
}
throw new InvalidOperationException(string.Format(CultureInfo.CurrentUICulture, MvcResources.DefaultControllerFactory_ControllerNameAmbiguous, new object[] { controllerName,
builder }));
}
第四:ControllerTypeCache:它的作用是缓存了所有的控制器的类型信息。
ControllerTypeCache._cache 保存了所有载入程序集中的 Controller 类型;
EnsureInitialized方法:依据 "controllerName(不包含 Controller 字符串)"和 对应的Namespace 进行二级分组。
ControllerTypeCache.GetControllerTypes() 首先通过 controllerName 提取一级分组,然后用 namespaces 参数提取最终的类型数组。如果没有提供 Namespaces 则返回所有的同名 ControllerType。
分组示例:
Home
Namespace1
Namespace1.HomeController
GuestBoke
Namespace1
Namespace1.HomeController
Namespace2
Namespace2.HomeController
ControllerTypeCache部分代码:
上面的代码中允许出现不同命令空间但controllerName相同的Controller,例如默认asp.net mvc 工程在创建时已经创建了一下HomeController,我们在GuestBook.MVC.Controller工程中再创建一个同名的HomeController。然后修改下HomeController中的Index:
{
ViewData["Message"] = "Welcome to ASP.NET MVC!";
return RedirectToAction("About");
//return View();
}
运行一下,程序并不会识别Contrller,错误信息如下:
GuestBook.MVC.Controller.HomeController
GuestBook.Web.Controllers.HomeController
解决方案:选择任意一种都行。
1:在路由规则中加了命名空间。
"Default", // Route name
"{controller}/{action}/{id}", // URL with parameters
new { controller = "Home", action = "Index", id = "" }, // Parameter defaults
new string[] { "GuestBook.MVC.Controller" }
);
2:ControllerBuilder.Current.DefaultNamespaces.Add("GuestBook.MVC.Controller");
总结:本文分析了Controller与路由的关系。
注:本文参考:http://www.rainsts.net/article.asp?id=777