第四章 功能初始化
源代码GitHub:https://github.com/ZhaoRd/Zrd_0001_AuthorityManagement
1.介绍
对于权限管理系统来说,系统模块是必须的一部分,那么如何处理和收集模块信息是一个管家步骤,在没有看郭民峰的osharp之前,我能想到的都是通过管理员通过后台管理进行管理,osharp里采用attirbute的方式采集模块信息。该demo借鉴osharp的方式,通过使用attribute定义模块信息,程序启动时通过判断attribute进行模块和具体功能权限的信息收集。
对于新采集的信息,如何判定这些新信息哪些是需要更新的,哪些是需要从已有信息中删除的,哪些又是需要新增的呢?这里需要使用到集合运算,具体请看以下内容。
2.模块信息的定义
在web中,操作一个功能的访问路径是 controller/action ,在本demo中,一个controller就是一个功能,一个action就是一个具体的操作权限。在源代码中,定义SystemModelAttribute来定义功能信息,代码如下:
/// <summary> /// 收集系统模块. /// </summary> public class SystemModelAttribute : Attribute { /// <summary> /// 模块名称. /// </summary> private string name; /// <summary> /// 分组名称. /// </summary> private string groupName; /// <summary> /// 只读模块名称. /// </summary> public string Name { get { return this.name; } } /// <summary> /// 分组名称. /// </summary> public string GroupName { get { return this.groupName; } set { // 如果是通过构造函数初始化的名称,则其他设置均无效 if (string.IsNullOrEmpty(this.groupName)) { this.groupName = value; } } } /// <summary> /// 模块的权限. /// </summary> private PermissionValue permissionValue; /// <summary> /// 模块的权限. /// </summary> public PermissionValue PermissionValue { get { return this.permissionValue; } } /// <summary> /// Initializes a new instance of the <see cref="SystemModelAttribute"/> class. /// </summary> /// <param name="name"> /// 模块名称. /// </param> /// <param name="permissionValue"> /// 模块权限值. /// </param> /// <param name="groupName"> /// 分组名称. /// </param> public SystemModelAttribute(string name, PermissionValue permissionValue = PermissionValue.All, string groupName = null) { this.name = name; this.groupName = groupName; this.permissionValue = permissionValue; } }
在controller上使用该attribute,即可定义一个模块,使用方式如下图
SystemModelAttribute可以定义需要采集的模块信息,同理,可以在定义一个attribute,来定义需要采集的权限信息,源代码如下
/// <summary> /// 权限信息设置,以便信息收集. /// </summary> public class PermissionSettingAttribute : Attribute { /// <summary> /// 具体权限. /// </summary> private readonly PermissionValue permissionValue; /// <summary> /// Initializes a new instance of the <see cref="PermissionSettingAttribute"/> class. /// </summary> /// <param name="value"> /// The value. /// </param> public PermissionSettingAttribute(PermissionValue value) { this.permissionValue = value; } /// <summary> /// Gets the permission value. /// </summary> public PermissionValue PermissionValue { get { return this.permissionValue; } } }
使用方式如下:
通过使用SystemModelAttribute和PermissionSettingAttribute来定义需要采集信息的内容.
3.信息采集
我们在Controller和Action上分别使用了不同的Attribute来定义信息,如何在代码中收集这些信息呢?主要代码如下
首先是获取到Controller所在的程序集,然后根据获得的类型处理Type,具体的Invoke方法如下
/// <summary> /// 解析Controller类型来收集Attribute信息. /// </summary> /// <param name="target"> /// The target. /// </param> /// <returns> /// The <see cref="IEnumerable"/>. /// </returns> public IEnumerable<FunctionDto> Invoke(Type target) { var result = new List<FunctionDto>(); var targetType = target; // 是否使用SystemModelAttribute if (!targetType.IsDefined(typeof(SystemModelAttribute), false)) { return result; } // 获取所有Controller里的方法 var methods = targetType.GetMethods(); // 获取功能模块的具体权限,必须是使用了PermissionSettingAttribute定义权限值,并取所有功能的并运算 // 例如:一个Controller里只定义了Create和Edit,那么这个功能模块的权限就是 Create|Edit var functionPermissionValue = (from methodInfo in methods where methodInfo.IsDefined(typeof(PermissionSettingAttribute), false) select methodInfo.GetCustomAttribute<PermissionSettingAttribute>()).Aggregate( PermissionValue.None, (current, permissionSetting) => current | permissionSetting.PermissionValue); // 获取SystemModelAttribute具体的信息 var description = targetType.GetCustomAttribute<SystemModelAttribute>(); var areaName = this.GetArea(target); var function = new FunctionDto() { FunctionName = description.Name, ModelName = areaName, PermissionValue = functionPermissionValue }; result.Add(function); return result; }
通过这样的一个函数,就可以采集到一个Controller里的权限信息,一个功能就是一个Controller,Controller里使用PermissionSettingAttribute定义的具体操作权限,所有定义的权限值取并运算,
如图所示,一个Controller里包含Create和Edit,那么就说明这个功能模块的最大权限是Create|Edit,这样,在进行权限分配的时候,只能对该功能所具有的权限进行分配,未具有的权限不能分配.
4. 信息处理
经过上面的处理,那么功能信息和权限信息都已经采集到了,将这些信息保存到数据库,即可快捷方便的初始化系统的功能信息。
在信息处理方面,主要使用了集合运算,为了对集合运算进行说明,我把已存在数据库里的功能信息集合定义为 A集合,新采集的集合定义为 B集合,那么对于B集合而言,怎么判断哪些是需要新添加到数据库,哪些是需要更新到数据库的,已存在数据库里的功能,哪些又是需要删除的呢?
a.对于需要添加的功能,可以理解为存在B集合但是不存在A集合
b.对于需要更新的功能,可以理解为即存在B集合又存在A集合
c.对于需要删除的功能,可以理解为存在A集合中但是不存在B集合中
从集合的角度来处理a b c三种情况,那么
a. B集合 减 A集合
b. B集合 交 A集合
c. A集合 减 B集合
通过集合运算,就可以得到需要进行添加、更新、删除的功能信息了
代码如下:
/// <summary> /// 初始化系统功能. /// </summary> /// <param name="functionDtos"> /// The function dtos. /// </param> public void InitModel(IEnumerable<FunctionDto> functionDtos) { var functions = this.functionRepository.FindAll().ToList(); var addFunctions = functionDtos.Select(Mapper.Map<FunctionDto, Function>) .AsEnumerable(); // 创建如何判断两个function是否相等的条件 var functionComparer = EqualityHelper<Function>.CreateComparer(m => m.ModelName + "#" + m.FunctionName); var enumerable = addFunctions as Function[] ?? addFunctions.ToArray(); // 包含在将要处理的集合(addFunctions) // 但不包含在已经存在的集合(functions) // 表示需要添加到系统里的模块 // 差集运算 var toAddFunctions = enumerable.Except(functions, functionComparer); // 包含在已经存在的集合(functions) // 但不包含在将要处理的集合(addFunctions) // 表示需要从系统中删除的模块 // 差集运算 var toDeleteFunctions = functions.Except(enumerable, functionComparer); // 即包含在将要处理的集合(addFunctions) // 又包含在已经存在的集合(functions) // 表示需要更新内容 // 交集运算 var toUpdateFunctions = functions.Intersect(enumerable, functionComparer); LogHelper.Logger.Info( string.Format( "新增功能:{0}条;更新功能:{1}条;删除功能:{2};", toAddFunctions.Count(), toUpdateFunctions.Count(), toDeleteFunctions.Count())); foreach (var addFunction in toAddFunctions) { addFunction.ID = Guid.NewGuid(); var role = this.roleRepository.Find( Specification<Role>.Eval(u => u.RoleName == "系统管理员")); this.functionRepository.Add(addFunction); // 初始化系统管理员的权限 this.functionInRoleRepository.Add(new FunctionInRole() { ID = GuidHelper.GenerateGuid(), Function = addFunction, PermissionValue = addFunction.PermissionValue, Role = role }); } foreach (var deleteFunction in toDeleteFunctions) { this.functionRepository.Remove(deleteFunction); } foreach (var updateFunction in toUpdateFunctions) { var function = updateFunction; var query = enumerable.Where(m => m.FunctionName == function.FunctionName); var newValue = string.IsNullOrEmpty(updateFunction.ModelName) ? query.SingleOrDefault(u => u.ModelName == null) : query.SingleOrDefault(u => u.ModelName == function.ModelName); if (newValue == null) { continue; } updateFunction.FunctionName = newValue.FunctionName; updateFunction.ActionName = newValue.ActionName; updateFunction.AreasName = newValue.AreasName; updateFunction.ControllerName = newValue.ControllerName; updateFunction.Description = newValue.Description; updateFunction.ModelName = newValue.ModelName; updateFunction.PermissionValue = newValue.PermissionValue; this.functionRepository.Update(updateFunction); } this.functionInRoleRepository.Context.Commit(); }
5. 总结
信息的定义和收集,主要是使用Attribute,该demo提供了一种使用Attribute的使用方式和方法。
该篇还提供了一个使用集合进行处理数据的方式,扩展了我再处理集合时的一些思路,以前总是通过for 循环来处理两个集合,那么通过集合运算,可以很方便的得到集合信息。
推荐QQ群:
278252889(AngularJS中文社区)
5008599(MVC EF交流群)
134710707(ABP架构设计交流群 )
59557329(c#基地 )
230516560(.NET DDD基地 )
本人联系方式:QQ:351157970