探索RegisterAllAreas
在MVC中注册Area时,我们一般会在相应的区域下定义一个继承与AreaRegistration的类,代码如下:
public class AdminAreaRegistration : AreaRegistration { public override string AreaName { get { return "Admin"; } } public override void RegisterArea(AreaRegistrationContext context) { context.MapRoute( "Admin_default", "Admin/{controller}/{action}/{id}", new { action = "Index", id = UrlParameter.Optional } ); } }
那么问题来了,类AdminAreaRegistration是在什么时候被实例化的,方法RegisterArea是何时被调用的?
1、Global中注册所有区域
AreaRegistration.RegisterAllAreas();
2、查看AreaRegistration.RegisterAllAreas源码
internal static void RegisterAllAreas(RouteCollection routes, IBuildManager buildManager, object state) { //从缓存以及组件中查找所有的AreaRegistration List<Type> filteredTypesFromAssemblies = TypeCacheUtil.GetFilteredTypesFromAssemblies("MVC-AreaRegistrationTypeCache.xml", new Predicate<Type>(AreaRegistration.IsAreaRegistrationType), buildManager); foreach (Type current in filteredTypesFromAssemblies) { AreaRegistration areaRegistration = (AreaRegistration)Activator.CreateInstance(current); areaRegistration.CreateContextAndRegister(routes, state); } }
3、从缓存及引用程序集中查找Type
public static List<Type> GetFilteredTypesFromAssemblies(string cacheName, Predicate<Type> predicate, IBuildManager buildManager) { TypeCacheSerializer serializer = new TypeCacheSerializer(); //从缓存中查找AreaRegistration List<Type> list = TypeCacheUtil.ReadTypesFromCache(cacheName, predicate, buildManager, serializer); if (list != null) { return list; } //从组件中查找AreaRegistration list = TypeCacheUtil.FilterTypesInAssemblies(buildManager, predicate).ToList<Type>(); //添加缓存 TypeCacheUtil.SaveTypesToCache(cacheName, list, buildManager, serializer); return list; }
4、从程序集中查找Type
private static IEnumerable<Type> FilterTypesInAssemblies(IBuildManager buildManager, Predicate<Type> predicate) { IEnumerable<Type> enumerable = Type.EmptyTypes; //获取所有被引用的组件 ICollection referencedAssemblies = buildManager.GetReferencedAssemblies(); foreach (Assembly assembly in referencedAssemblies) { Type[] types; try { //获取组件中的所有类型 types = assembly.GetTypes(); } catch (ReflectionTypeLoadException ex) { types = ex.Types; } enumerable = enumerable.Concat(types); } //返回所有类型中的AreaRegistration return from type in enumerable where TypeCacheUtil.TypeIsPublicClass(type) && predicate(type) select type; }
5、此处使用predicate(type),不得不回到步骤2看看传入的是啥
private static bool IsAreaRegistrationType(Type type) { return typeof(AreaRegistration).IsAssignableFrom(type) && type.GetConstructor(Type.EmptyTypes) != null; }
6、此时已找到所有的AreaRegistration类,再次回到步骤2,创建AreaRegistration实例,并调用方法RegisterArea
internal void CreateContextAndRegister(RouteCollection routes, object state) { AreaRegistrationContext areaRegistrationContext = new AreaRegistrationContext(this.AreaName, routes, state); string @namespace = base.GetType().Namespace; if (@namespace != null) { areaRegistrationContext.Namespaces.Add(@namespace + ".*"); } this.RegisterArea(areaRegistrationContext); }
由以上代码可以看书,方法RegisterAllAreas就是简单粗暴的将所有引用程序集中的类型遍历一遍,找到所有的AreaRegistration,并调用注册区域方法。
为了验证以上观点,特做了以下测试。
新建一个类库程序,并在类库程序中注册区域,观察是否注册成功。文件结构如下
测试结果:
由于本人也是最近才接触MVC,在学习过程中碰到一些问题,特此备注
1、Mvc定义区域外层文件夹约定为Areas
2、Views文件夹下一定要有web.config,否则会报错(当前上下文中不存在名称“ViewBag”),可以复制项目Views下自动生成的Web.config
3、注册区域可以在其他被引用的项目中(AreaRegistration)