asp.net mvc(五)

      我们在创建一个Controller时都会默认遵守这样的规定,名称+“Controller”,但我们在页面访问这个Controller时并不会写上后面的Controller字符,为什么系统能自动识别呢?这里我们也可以从Controller源码中得知。

      第一:Controller中的重要方法:CreateController,由它负责创建具体的Controller。这个方法根据用户传的controllerName(例如:Home)来取得实际的Controller的类型,其中用到这样的方法:this.GetControllerType(controllerName)。

public virtual IController CreateController(RequestContext requestContext, string 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来取。

protected internal virtual Type GetControllerType(string 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中的第一个类型,如果没有取到则返回空,如果查找到多个则返回第一个类型。

private Type GetControllerTypeWithinNamespaces(string controllerName, HashSet<string> namespaces)
{
    
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部分代码:

Code

         

       上面的代码中允许出现不同命令空间但controllerName相同的Controller,例如默认asp.net mvc 工程在创建时已经创建了一下HomeController,我们在GuestBook.MVC.Controller工程中再创建一个同名的HomeController。然后修改下HomeController中的Index:

public ActionResult Index()
        {
            ViewData[
"Message"= "Welcome to ASP.NET MVC!";
            
return RedirectToAction("About");
            
//return View();
        }

      
      运行一下,程序并不会识别Contrller,错误信息如下:

 

The controller name 'Home' is ambiguous between the following types:
GuestBook.MVC.Controller.HomeController
GuestBook.Web.Controllers.HomeController


      解决方案:选择任意一种都行。

          1:在路由规则中加了命名空间。

  routes.MapRoute(
                
"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
 

posted on 2009-08-12 17:29  min.jiang  阅读(3413)  评论(2编辑  收藏  举报