按步骤看:
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; }
这个接口要求含有一个菜单字典和一个主菜单定义
这个类的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 vNext 源码分析] - 文章目录
一.简要介绍 ABP vNext 是 ABP 框架作者所发起的新项目,截止目前 (2019 年 2 月 18 日) 已经拥有 1400 多个 Star,最新版本号为 v 0.16.0 ,但还属于预览版 ...
- [Abp vNext 源码分析] - 5. DDD 的领域层支持(仓储、实体、值对象)
一.简要介绍 ABP vNext 框架本身就是围绕着 DDD 理念进行设计的,所以在 DDD 里面我们能够见到的实体.仓储.值对象.领域服务,ABP vNext 框架都为我们进行了实现,这些基础设施都 ...
- [Abp vNext 源码分析] - 3. 依赖注入与拦截器
一.简要说明 ABP vNext 框架在使用依赖注入服务的时候,是直接使用的微软提供的 Microsoft.Extensions.DependencyInjection 包.这里与原来的 ABP 框架 ...
- [Abp vNext 源码分析] - 2. 模块系统的变化
一.简要说明 本篇文章主要分析 Abp vNext 当中的模块系统,从类型构造层面上来看,Abp vNext 当中不再只是单纯的通过 AbpModuleManager 来管理其他的模块,它现在则是 I ...
- [Abp vNext 源码分析] - 1. 框架启动流程分析
一.简要说明 本篇文章主要剖析与讲解 Abp vNext 在 Web API 项目下的启动流程,让大家了解整个 Abp vNext 框架是如何运作的.总的来说 ,Abp vNext 比起 ABP 框架 ...
- [Abp vNext 源码分析] - 4. 工作单元
一.简要说明 统一工作单元是一个比较重要的基础设施组件,它负责管理整个业务流程当中涉及到的数据库事务,一旦某个环节出现异常自动进行回滚处理. 在 ABP vNext 框架当中,工作单元被独立出来作为一 ...
- [Abp vNext 源码分析] - 6. DDD 的应用层支持 (应用服务)
一.简要介绍 ABP vNext 针对于应用服务层,为我们单独设计了一个模块进行实现,即 Volo.Abp.Ddd.Application 模块. PS:最近博主也是在恶补 DDD 相关的知识,这里推 ...
- [Abp vNext 源码分析] - 7. 权限与验证
一.简要说明 在上篇文章里面,我们在 ApplicationService 当中看到了权限检测代码,通过注入 IAuthorizationService 就可以实现权限检测.不过跳转到源码才发现,这个 ...
- [Abp vNext 源码分析] - 9. 接口参数的验证
一.简要说明 ABP vNext 当中的审计模块早在 依赖注入与拦截器一文中有所提及,但没有详细的对其进行分析. 审计模块是 ABP vNext 框架的一个基本组件,它能够提供一些实用日志记录.不过这 ...
随机推荐
- Step by step Install a Local Report Server and Remote Report Server Database
原创地址:http://www.cnblogs.com/jfzhu/p/4012097.html 转载请注明出处 前面的文章<Step by step SQL Server 2012的安装 &g ...
- Linux服务器配置之加载硬盘
Linux服务器配置之加载硬盘 1.修改密码 passwd 2.测试密码是否成功 3.查看硬盘信息 fdisk –l 4.格式化分区 fdisk /dev/vdb 5.查看分区 6.快速格式化/dev ...
- [C#] Socket 通讯,一个简单的聊天窗口小程序
Socket,这玩意,当时不会的时候,抄别人的都用不好,简单的一句话形容就是“笨死了”:也是很多人写的太复杂,不容易理解造成的.最近在搞erlang和C的通讯,也想试试erlang是不是可以和C#简单 ...
- C#设计模式系列:组合模式(Composite)
1.组合模式简介 1.1>.定义 组合模式主要用来处理一类具有“容器特征”的对象——即它们在充当对象的同时,又可以作为容器包含其他多个对象. 1.2>.使用频率 中高 2.组合模式结构图 ...
- C#设计模式系列:开闭原则(Open Close Principle)
1.开闭原则简介 开闭原则对扩展开放,对修改关闭,开闭原则是面向对象设计中可复用设计的基石. 2.开闭原则的实现 实现开闭原则的关键就在于抽象,把系统的所有可能的行为抽象成一个抽象底层,这个抽象底层规 ...
- 移动web app开发必备 - Deferred 源码分析
姊妹篇 移动web app开发必备 - 异步队列 Deferred 在分析Deferred之前我觉得还是有必要把老套的设计模式给搬出来,便于理解源码! 观察者模式 观察者模式( 又叫发布者-订阅者模 ...
- WPF 弹出UserControl
UserControl正常情况下是不能被弹出的,而编写好的UserControl页面,为了查看效果,又需要弹出. 为了解决这个问题,UserControl需要一个Windows来接收. var win ...
- 在usercontrol里实现导航
(Application.Current.RootVisual as PhoneApplicationFrame).Navigate(new Uri("/NewContent.xaml&qu ...
- 将 instance 连接到 first_local_net - 每天5分钟玩转 OpenStack(82)
上一节 first_local_net 已经就绪,下面创建 instance 并将其连接到该网络. 将 instance 连接到 first_local_net launch 一个 instance, ...
- angularjs 锚点操作服务 $anchorScroll
在普通的html网页中,我们可以通过在url后边添加 #elementid 的方式,将页面显示定位到某个元素,也就是锚点. 但是在angularjs应用的网页中,页面路由的写法是 #route/ro ...