由于在实际的工作中, 碰见这样的一个问题:
一个软件, 销售给A客户 他需要所有功能,
但是销售给B客户, 他只需要其中的一部分,
1.如果我们在实际的开发过程中, 没有把一些功能模块区分开来的话, 那么带来的麻烦, 势必是要修改源代码。
2.直到有一天,B客户又说需要某些功能,这个时候, 我们又要修改一次源代码, 更新给客户 , 所以想了想, 如果每个功能块都独立开来, 动态去加载功能, 这样就不用改动源代码, 客户需要哪些功能, 注册那些DLL给他们使用。
?.实现思路
1.每个模块都用单独的程序集(DLL)分开 <反射动态加载>
2.需要指定每个功能模块的命名空间 <特性标记命名空间>
!.实现代码
1.程序中定义自定义特性, 用于映射程序的的模块信息和功能模块所在的命名空间
/// <summary> /// 模块编号. /// </summary> public enum ModuleID { None = 0, DataDictionary = 1, SystemManage = 2 } /// <summary> /// 模块名称. /// </summary> public class ModuleNames { public const string DataDictionary = "基础数据"; public const string SystemManage = "系统管理"; } /// <summary> /// 模块入口自定义特性 /// </summary> public class AssemblyModuleEntry : Attribute { private ModuleID _moduleID; private string _moduleName; private string _moduleEntryNameSpace; /// <summary> /// 模块编号 /// </summary> public ModuleID ModuleID { get { return _moduleID; } } /// <summary> /// 模块名称 /// </summary> public string ModuleName { get { return _moduleName; } } /// <summary> /// 模块名字空间 /// </summary> public string ModuleEntryNameSpace { get { return _moduleEntryNameSpace; } } /// <summary> /// 构造器 /// </summary> /// <param name="moduleID">模块编号</param> /// <param name="moduleName">模块名称</param> /// <param name="moduleEntryNameSpace">模块名字空间</param> public AssemblyModuleEntry(ModuleID moduleID, string moduleName, string moduleEntryNameSpace) { _moduleID = moduleID; _moduleName = moduleName; _moduleEntryNameSpace = moduleEntryNameSpace; } }
在模块的程序集设置好新建的自定义特性 (如下):
// 有关程序集的一般信息由以下 // 控制。更改这些特性值可修改 // 与程序集关联的信息。 [assembly: AssemblyModuleEntry(ModuleID.DataDictionary, ModuleNames.DataDictionary, "My.DataDictionary.Form1")]
1.定义方法读取程序集(DLL)中的第一个特性信息
/// <summary> /// 获取程序集自定义特性。是否用户自定义模块由AssemblyModuleEntry特性确定。 /// </summary> public static AssemblyModuleEntry GetModuleEntry(Assembly asm) { AssemblyModuleEntry temp = new AssemblyModuleEntry(ModuleID.None, "", ""); if (asm == null) return temp; object[] list = asm.GetCustomAttributes(typeof(AssemblyModuleEntry), false); if (list.Length > 0) return (AssemblyModuleEntry)list[0]; else return temp; }
2.保存AssemblyModuleEntry 的特性信息, 解析特性信息利用反射映射功能模块的主窗体
/// <summary> /// 加载模块主方法 /// </summary> /// <param name="moduleinfo">模块信息</param> /// <returns></returns> public virtual bool LoadModule(ModuleInfo moduleinfo) { _ModuleFileName = moduleinfo.ModuleFile; _ModuleAssembly = moduleinfo.ModuleAssembly; string entry = GetModuleEntryNameSpace(_ModuleAssembly); if (string.Empty == entry) return false; Form form = (Form)_ModuleAssembly.CreateInstance(entry); //根据命名空间加载Form _ModuleMainForm = null; if (form is IModuleBase) _ModuleMainForm = (IModuleBase)form; return _ModuleMainForm != null; }
3.将功能模块的功能都加载到全局缓存对象中, 创建首页的UI控件, 将缓存对象中的模块加载到首页中。
实际效果图:
注意: 反射是会带来性能的损耗, 但是经过合理的优化,还是对性能影响不大, 当个这个设计, 主要看每个人使用的取舍。
核心思想:
1.定义自定义特性
2.将功能命名空间存储到自定义特性中(主要用于反射获取到指定的功能界面区)
3.利用反射去获取目录下的有特性的程序集,加载指定的功能
4.将反射获取的指定界面转换成缓存对象
5.将缓存对象转换成首页指定的UI控件上。
PS: 关于权限的控制思路:
针对所登陆得用户, 获取当前用户所有的权限, 根据权限加载权限内的程序集。
---恢复内容结束---