在Mvc2.0中,新增加了一个特性就是Areas。在没有有使用Areas的情况下,我们的Mvc项目组织是下面这样的。当项目庞大的时候,Controllers,Model,View文件下下面势必会有很多文件。项目将难以管理。
通过使用Areas使我们可以很好的组织项目,通过单机添加Areas(区域),使用Areas来组织项目。可以得到新的项目组织结构。
First,Second对应着我们项目的子模块(First,Second命名不是很好)。在这两个文件夹下,有各自独立的Controllers,Models,Views。此外还多了个文件AreaRegistration为后缀的.cs文件. 这个文件主要的作用是给Areas下的子模块配置路由。在全局文件Global.asax中的Application_Start事件里有这么一句代码 AreaRegistration.RegisterAllAreas(),通过mvc源码可以发现通过这句代码最后会调用各个AreaRegistration类,实现路由的注册。
//重写了AreaName用于给DataToken["area"]赋值public class FirstAreaRegistration : AreaRegistration{public override string AreaName{get{return "First";}}//这里会添加一个相应的Area名称的前缀,因为下面这样的添加也是作用到全局路由表的.//而且这里的MapRoutes,和在全局的MapRoutes是不同的//这时AreaRegistrationContext里面的方法,它里面的方法会自动给DataToken["area"]键赋值当前的AreaName public override void RegisterArea(AreaRegistrationContext context){context.MapRoute("First_default","First/{controller}/{action}/{id}",new { controller = "Home", action = "Index", id = UrlParameter.Optional });}
当调用 AreaRegistration.RegisterAllAreas()时,会最后调用下面这个方法。通过下面的代码可以得出,这个方法会通过反射得出所有的AreaRegistration的Type实例。接下来依次通过Activator创建实例,进而调用CreateContentAndRegister方法。\
internal static void RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, object state) {List<Type> areaRegistrationTypes = TypeCacheUtil.GetFilteredTypesFromAssemblies(_typeCacheName, IsAreaRegistrationType, buildManager);foreach (Type areaRegistrationType in areaRegistrationTypes) {AreaRegistration registration = (AreaRegistration)Activator.CreateInstance(areaRegistrationType);registration.CreateContextAndRegister(routes, state);}}
registration.CreateContextAndRegister方法:
internal void CreateContextAndRegister(RouteCollection routes, object state) {//实例化一个AreaRegistrationContext 实例,AreaName已经传递给AreaRegistrationContext了AreaRegistrationContext context = new AreaRegistrationContext(AreaName, routes, state);string thisNamespace = GetType().Namespace;if (thisNamespace != null) {context.Namespaces.Add(thisNamespace + ".*");}//调用注册方法,这个方法在各个AreaRegistration中被重写 RegisterArea(context);}
各个AreaRegistration重写的RegisterArea(context)方法里面通过调用AreaRegistrationContext的Maproute方法来实现注册,这个方法最后的调用方法是下面这个。可以发现下面这个方法也会调用RouteCollection的MapRoute方法,调用这个方法之后,又向route的DataTokens字典的area和UseNamespaceFallback键设置值。这有什么作用?
public Route MapRoute(string name, string url, object defaults, object constraints, string[] namespaces) {if (namespaces == null && Namespaces != null) {namespaces = Namespaces.ToArray();}Route route = Routes.MapRoute(name, url, defaults, constraints, namespaces);route.DataTokens["area"] = AreaName;// disabling the namespace lookup fallback mechanism keeps this areas from accidentally picking up// controllers belonging to other areasbool useNamespaceFallback = (namespaces == null || namespaces.Length == 0);route.DataTokens["UseNamespaceFallback"] = useNamespaceFallback;return route;}
还有几个疑问待解决:
1.Area下的路由过程是怎么样的?
2.在Area下可以实现不同的Area有相同的ControllerName,可以在全局的Global.ascx进行文件配置,但是如果是同名的Controller则会出现错误,而对没有同名的Controller进行配置则不会,这是为什么?
比如在First,Second都有HomeController,如果在全局的Global.ascx文件进行配置,下面的配置不会出错,但是如果namespaces是MvcAreaDemo2.Areas.First时则会出现错误。这时为什么?如果没有用同名的Controller则namespace是MvcAreaDemo2.Areas.First和MvcAreaDemo2.Areas.First.Controllers都没有错。
routes.Add(new Route("Index1", new MvcRouteHandler()){Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = UrlParameter.Optional }),DataTokens = new RouteValueDictionary(new { area = "First", namespaces = new[] { "MvcAreaDemo2.Areas.First.Controllers" } })});routes.Add(new Route("Index2", new MvcRouteHandler()){Defaults = new RouteValueDictionary(new { controller = "Home", action = "Index", id = UrlParameter.Optional }),DataTokens = new RouteValueDictionary(new { area = "Second", namespaces = new[] { "MvcAreaDemo2.Areas.Second.Controllers" } })});
3.在Area的使用况下,如果要跳转到另外一个Area的页面,则使用Html.ActionLink进行编写似乎更加麻烦了,有木有更加简便的方法?