由於在實際的工作中, 碰見這樣的一個問題:
一個軟件, 銷售給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: 關於權限的控制思路:
針對所登陸得用戶, 獲取當前用戶所有的權限, 根據權限加載權限內的程序集。
---恢復內容結束---