按步骤看:
1,在Global.asax中执行:
base.Application_Start(sender, e);
2,在AbpWebApplication类的Application_Start()中执行:
AbpBootstrapper.Initialize();
3,在AbpBootstrapper.Initialize()中执行:
IocManager.Resolve<AbpStartupConfiguration>().Initialize();
4,在AbpStartupConfiguration.Initialize()中执行:
 Navigation = IocManager.Resolve<INavigationConfiguration>();
至此,我们获得一个INavigationConfiguration实例,该实例拥有一个NavigationProvider列表(暂时是空列表)
5,回到第3步,在AbpBootstrapper.Initialize()中继续执行:
 _moduleManager.InitializeModules();
6,在AbpModuleManager的InitializeModules()中执行:
sortedModules.ForEach(module => module.Instance.PreInitialize());
也就是所有模块的PreInitialize()方法都逐个执行一遍。
7,于是转到XXXWebModule(XXX为应用程序名)模块的PreInitialize()方法会执行:
 Configuration.Navigation.Providers.Add<XXXNavigationProvider>();
至此,原来还是空的NavigationProvider列表被填充进来一个XXXNavigationProvider。
8,回到第6步,在AbpModuleManager的InitializeModules()中继续执行:
 sortedModules.ForEach(module => module.Instance.PostInitialize());
也就是所有模块的PostInitialize()方法都逐个执行一遍。
9,于是转到AbpKernelModule模块的PostInitialize()方法,执行:
 IocManager.Resolve<NavigationManager>().Initialize();
10,NavigationManager的Initialize()方法遍历NavigationProvider列表,并执行NavigationProvider的SetNavigation()方法。
11,在XXXNavigationProvider的SetNavigation()方法里,构造了一个菜单的树状结构。
 
按接口和类看:
IHasMenuItemDefinitions接口:
声明了属性:IList<MenuItemDefinition> Items { get; }
这个接口要求必须含有一个MenuItemDefinition列表。
实现类:MenuDefinition、MenuItemDefinition
前者是根菜单,后者是子菜单,而子菜单也还包含子菜单,形成树状结构。
两个类都有AddItem方法。
MenuDefinition只增加了Name和DisplayName两个属性,而MenuItemDefinition更增加了Order、Icon、Url、RequiredPermissionName、RequiresAuthentication、IsLeaf属性。
两个类都实现IhasMenuItemDefinitions接口的好处,体现在HasMenuItemDefinitionsExtensions类中的两个方法:
GetItemByName、GetItemByNameOrNull,这两个方法都是根据传入的IhasMenuItemDefinitions对象和菜单项名称去查询一个MenuItemDefinition对象。遍历IhasMenuItemDefinitions对象下的Items,加上使用递归,就能实现这个查询了。
若获得的MenuItemDefinition为null,第一个方法会抛出异常,第二个方法直接返回null
另外,MenuItemDefinitionExtensions类为处理MenuItemDefinition增加了一些实用方法。
不过HasMenuItemDefinitionsExtensions和MenuItemDefinitionExtensions里定义的方法暂时都没用使用到,先不用理会。
注意MenuDefinition、MenuItemDefinition类都是对菜单的定义(Definition),具体类是UserMenu和UserMenuItem
查看两个具体类的构造函数就知道它们是通过对应的定义类来初始化的。
(菜单定义和用户菜单二者必须分开,原因很简单,用户菜单由于权限等原因,可能会排除掉一些对于特定用户来说无权限的菜单项,也就是对该用户不予展示,因此菜单定义和用户菜单需要区别对待。详情参看UserNavigationManager类的FillUserMenuItems方法)
 
INavigationManager接口:
 IDictionary<string, MenuDefinition> Menus { get; }
MenuDefinition MainMenu { get; }
这个接口要求含有一个菜单字典和一个主菜单定义
实现类:NavigationManager
这个类的Initialize()方法在AbpKernelModule类的PostInitialize方法中被调用:
 IocManager.Resolve<NavigationManager>().Initialize();
该方法的作用是遍历NavigationProvider(导航菜单的数据提供者)列表,并执行NavigationProvider的SetNavigation方法,如此一来,Menus和MainMenu就填充进数据了。
 
INavigationProviderContext接口:
INavigationManager Manager { get; }
这个接口要求含有一个INavigationManager实例。
实现类:NavigationProviderContext
很简单,就是通过构造函数传参,初始化Manager (IoC注入)
NavigationProvider抽象类:
声明了一个 SetNavigation(INavigationProviderContext context)方法。
这个抽象类是需要在具体项目中上实现的,例如在项目XXX.Web下:
    public class XXXNavigationProvider : NavigationProvider
    {
        public override void SetNavigation(INavigationProviderContext context)
        {
            context.Manager.MainMenu
                .AddItem(
                    new MenuItemDefinition(
                        "Home",
                        new LocalizableString("HomePage", XXXConsts.LocalizationSourceName),
                        url: "/",
                        icon: ""
                        )
                );
        }
    }
 
 
INavigationConfiguration接口:
ITypeList<NavigationProvider> Providers { get; }
这个接口声明了一个NavigationProvider列表。
实现类:NavigationConfiguration
也就是在构造函数里简单地初始化Providers 为一个空列表。
对这个列表的填充,是在XXXWebModule的PreInitialize()中进行的:
Configuration.Navigation.Providers.Add<XXXNavigationProvider>();

当然,我们在这里还可以填充更多的Provider,考虑到一个网站不止一个导航结构,可能还有第二个、第三个导航结构。

XXXNavigationProvider 使用代码方法构造一个树状结构,当然也可以通过读取XML配置来构造之。
Providers 会在NavigationManager中被遍历,并执行其SetNavigation方法,初始化导航的菜单定义及其树状结构。
 
IUserNavigationManager接口:
 Task<UserMenu> GetMenuAsync(string menuName, long? userId);
Task<IReadOnlyList<UserMenu>> GetMenusAsync(long? userId);
这个接口声明了获取用户菜单的两个方法,前者获取一个菜单,后者获取菜单列表。
实现类:UserNavigationManager
GetMenuAsync方法的实现:通过传入的menuName,在INavigationManager实例的Menus中获取对应的MenuDefinition对象,然后使用这个对象来初始化一个UserMenu
这还不够,因为UserMenu内还会包含子菜单,也就是UserMenu的Items属性需要初始化。
查看代码可知,在FillUserMenuItems方法内,遍历了MenuDefinition对象内的子菜单定义列表,对用户的权限进行了检查,如有权限,就加入到UserMenu的子菜单里。通过递归FillUserMenuItems,用户菜单的树状结构也就建立了。
GetMenusAsync方法的实现:遍历INavigationManager实例的Menus,把所有根菜单都通过上面的GetMenuAsync方法初始化出对应的UserMenu对象。这个方法正是Web项目中使用到的方法,在LayoutController控制器中可以看到:
        public PartialViewResult TopMenu(string activeMenu = "")
        {
            var model = new TopMenuViewModel
                        {
                            MainMenu = AsyncHelper.RunSync(() => _userNavigationManager.GetMenuAsync("MainMenu", CurrentSession.UserId)),
                            ActiveMenuItemName = activeMenu
                        };
 
            return PartialView("_TopMenu", model);
        }

ABP导航源码分析的更多相关文章

  1. [Abp vNext 源码分析] - 文章目录

    一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...

  2. [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)

    一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...

  3. [Abp vNext 源码分析] - 3. 依赖注入与拦截器

    一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...

  4. [Abp vNext 源码分析] - 2. 模块系统的变化

    一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...

  5. [Abp vNext 源码分析] - 1. 框架启动流程分析

    一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...

  6. [Abp vNext 源码分析] - 4. 工作单元

    一.简要说明 统一工作单元是一个比较重要的基础设施组件,它负责管理整个业务流程当中涉及到的数据库事务,一旦某个环节出现异常自动进行回滚处理. 在 ABP vNext 框架当中,工作单元被独立出来作为一 ...

  7. [Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)

    一.简要介绍 ABP vNext 针对于应用服务层,为我们单独设计了一个模块进行实现,即 Volo.Abp.Ddd.Application 模块. PS:最近博主也是在恶补 DDD 相关的知识,这里推 ...

  8. [Abp vNext 源码分析] - 7. 权限与验证

    一.简要说明 在上篇文章里面,我们在 ApplicationService 当中看到了权限检测代码,通过注入 IAuthorizationService 就可以实现权限检测.不过跳转到源码才发现,这个 ...

  9. [Abp vNext 源码分析] - 9. 接口参数的验证

    一.简要说明 ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析. 审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录.不过这 ...

随机推荐

  1. TODO:Ubuntu下安装Node

    TODO:Ubuntu下安装Node Node.js 是一个基于 Chrome V8 引擎的 JavaScript 运行环境.Node.js 使用了一个事件驱动.非阻塞式 I/O 的模型,使其轻量又高 ...

  2. web应用中使用JavaMail发送邮件

    现在很多的网站都提供有用户注册功能, 通常我们注册成功之后就会收到一封来自注册网站的邮件.邮件里面的内容可能包含了我们的注册的用户名和密码以及一个激活账户的超链接等信息.今天我们也来实现一个这样的功能 ...

  3. error LNK2038: 检测到“RuntimeLibrary”的不匹配项: 值“MDd_DynamicDebug”不匹配值“MTd_StaticDebug”

    今日VS2012 C++编译出现一堆错误: > 正在生成代码... >Code.obj : error LNK2005: "public: __thiscall std::_Co ...

  4. CRM/ERP 企业管理软件中常见的七种程序设计模式

    管理软件中的常见代码设计模式,来自于业务上的需要,有不恰当的地方欢迎批评指正. 1  RE-TRY 重试模式 场景:在连接数据库服务器时,如果SQL Server数据库没有启动或正在启动,我们需要有一 ...

  5. ASP.NET MVC5 网站开发实践(二) Member区域 - 用户部分(1)用户注册

    上一次把基本框架搭建起来了,这次开始整Web部分,终于可以看到界面了小激动一下.web项目部分从用户功能开始,基本有注册,登录.注销.查找.查看.删除等涉及Member区域和Manage区域. 目录: ...

  6. .NET平台开源项目速览(9)软件序列号生成组件SoftwareProtector介绍与使用

    在文章:这些.NET开源项目你知道吗?让.NET开源来得更加猛烈些吧!(第二辑)中,给大家初步介绍了一下Software Protector序列号生成组件.今天就通过一篇简单的文章来预览一下其强大的功 ...

  7. OracleDBA之用户管理

    再分享一下Oracle中对用户的管理,以下这些东西是我的麦库上存的当时学Oracle的学习笔记今天拿出来和大家分享一下,转载请注明出处,下面用的Oracle的版本是10g,用的时WinServer20 ...

  8. .Net中DataAdapter批量插入和更新数据总结

    前言 前段时间一直在忙着项目上线,在做项目的同时遇到了一些之前不曾碰到的问题,因为没有经验,只能从网上找一些相关的解决方案,但是网上提供的资料实在是太杂,有的根本不能用,耗时又耗力. 我希望把我这段时 ...

  9. ViewStub的使用

    ViewStub是一个不可见的.大小为0的控件,运行时ViewStub可以滞后加载.当ViewStub置为可见或者调用inflate()的时候,布局就会加载出来.用加载进来的布局取代ViewStub在 ...

  10. machine learning基础与实践系列

    由于研究工作的需要,最近在看机器学习的一些基本的算法.选用的书是周志华的西瓜书--(<机器学习>周志华著)和<机器学习实战>,视频的话在看Coursera上Andrew Ng的 ...