探索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)

 

posted @ 2016-07-03 21:23  NikLiu  阅读(2854)  评论(1编辑  收藏  举报