每一個業務系統都會根據業務需要配置各種各樣的權限,實現方式也是千差萬別,各有各的優缺點。今天我們
利用反射來做一個小的權限管理Demo。也可以說是插件化的權限管理,通用的插件化框架是實現一個接口或者協定,
我們的做法是先展示指定的數據,再去動態的加載需要用到的dll和功能。
大致的思路是這樣的,我們從服務或者從數據庫里讀取哪些dll需要加載,相應的dll下哪些頁面可以調用。把這些內容
動態的添加到頁面上,當點擊頁面上的元素時利用反射,匹配目錄下的dll和dll內的頁面,進行讀取,並顯示進行交互,
從面實現插件化動態加載內容。如下圖所示:
例:
》.首先我們先建一個工程ReflectionPermissionDemo
再添加3個類庫ReflectionPermissionDemo.A;ReflectionPermissionDemo.B;ReflectionPermissionDemo.C;
需要注意的是不要在ReflectionPermissionDemo引用后邊新建好的3個類庫。我們的目標是用反射去加載這些dll庫,
而不是使用引用方式。
創建完后還需要修改他們的輸出路徑屬性
ReflectionPermissionDemo就生成到bin目錄下
其它ReflectionPermissionDemo.A;ReflectionPermissionDemo.B;ReflectionPermissionDemo.C三個都指向這個
目錄下生成。這樣的目的是讓所有的dll都在同一個文件夾下。
》.創建一些頁面
ReflectionPermissionDemo項目里修改一下頁面。在頂部添加一個Panel用來動態添加按鈕,這些動態添加上的按鈕就是我們利
用反射動態加載的dll庫。在下邊也添加一個Panel用於動態顯示權限信息,對應是dll庫里面有權限的頁面。設置他們的
性Dock一個為Top下邊的為Fill。並給他們命名上邊的panelTop。叫下邊的叫panelBody
在ReflectionPermissionDemo.A;ReflectionPermissionDemo.B;ReflectionPermissionDemo.C各自創建一些頁面,這些頁面也
需要權限設置的允許才能打開。
在每個頁面上放一個標識,標明這個窗體與其它的窗體不同,可以根據個人需要,我這里放的是label寫了一些文字標明每個窗體是
哪個項目的哪個窗體,如下圖:
》模擬權限數據
寫一個單例類,創建一些權限數據,用於模擬從服務器上返回的數據。返回的數據有模塊id,模塊名稱,命名空間和父id。
我們可以把這些數結構想像成一棵樹結構。
/// <summary> /// 模擬遠程服務器 /// 返回擁有的權限 /// </summary> public class RemoteService { public static readonly RemoteService PermissionService = new RemoteService(); public DataTable PermissionTable { get; private set; } #region 字段名稱 public readonly string ModuleId = @"ModuleID"; public readonly string ModuleName = @"ModuleName"; public readonly string PermissioniNameSpace = @"PermissioniNameSpace"; public readonly string ParentId = @"ParentID"; #endregion private RemoteService() { AllPermision(); } /// <summary> /// 所有的權限 /// </summary> /// <returns></returns> private DataTable AllPermision() { PermissionTable = new DataTable(); #region Permission Page PermissionTable.Columns.AddRange(new[] { new DataColumn(ModuleId, typeof (Int32)), // 模塊id new DataColumn(ModuleName, typeof (string)), // 模塊名稱 new DataColumn(PermissioniNameSpace, typeof (string)), // 命名空間 new DataColumn(ParentId, typeof (Int32)) // 父id }); #endregion #region A CreateNewRow(1001, @"A模塊", @"A", -1); CreateNewRow(1002, @"A 頁面1", @"A.AForm1", 1001); // 測試權限先注掉 //CreateNewRow(1003, @"A 頁面2", @"A.AForm2", 1001); #endregion #region B 由於我們的例子只不需要B的權限,這里先注掉 //CreateNewRow(2001, @"B模塊", @"B", -1); //CreateNewRow(2002, @"B 頁面1", @"B.BForm1", 2001); #endregion #region C CreateNewRow(3001, @"C模塊", @"C", -1); CreateNewRow(3002, @"C Page1", @"C.CForm1", 3001); CreateNewRow(3003, @"C Page2", @"C.CForm2", 3001); #endregion return null; } /// <summary> /// 添加行 /// </summary> /// <param name="moduleId"></param> /// <param name="moduleName"></param> /// <param name="perNameSpace"></param> /// <param name="parentId"></param> private void CreateNewRow(int moduleId, string moduleName, string perNameSpace, int parentId) { var newRow = PermissionTable.NewRow(); newRow[ModuleId] = moduleId; newRow[ModuleName] = moduleName; newRow[PermissioniNameSpace] = perNameSpace; newRow[ParentId] = parentId; PermissionTable.Rows.Add(newRow); } }
》根據權限動態加載頁面和利用反射去打開相應的頁面
先整理一下思路。
1.讀取模擬的權限數據。
2.根據權限數據組織頁面元素
》有哪些dll可以被加載
》相應的dll內有哪些頁面可以被調用
3.點擊頁面元素根據保存的數據利用反射把頁面展示出來。
——————————————————
>先添加一些字段。
// 權限數據 readonly DataTable _permissionDt = RemoteService.PermissionService.PermissionTable; // 可用權限動態生成的panel頁面 readonly Dictionary<int, FlowLayoutPanel> _pagePanels = new Dictionary<int, FlowLayoutPanel>(); // 已加載過的頁面 private readonly Dictionary<string, Type> _formTypes = new Dictionary<string, Type>();
>根據權限數據動態組織頁面
private void Main_Load(object sender, EventArgs e) { var query = _permissionDt.Rows.Cast<DataRow>(); var parentData = query.Where(x => int.Parse(x[RemoteService.PermissionService.ParentId].ToString()) == -1); SettingDllButtons(parentData); } /// <summary> /// 展示 dll的權限按鈕 /// </summary> /// <param name="dt"></param> private void SettingDllButtons(IEnumerable<DataRow> drs) { int width = 80, height = 30, x = 0, y = 0; foreach (var dataRow in drs) { var btn = new Button { Text = dataRow[RemoteService.PermissionService.ModuleName].ToString(), Size = new Size(width, height), Location = new Point(x, y) }; var index = SettingPageButtons(dataRow); btn.Tag = index; btn.Click += btnDLL_Click; panelTop.Controls.Add(btn); x += width + 10; } } /// <summary> /// 根據 datarow的父id去找到所有的子節點 /// 加載到相應的頁面上組織成按鈕 /// </summary> /// <param name="dr"></param> /// <returns></returns> private int SettingPageButtons(DataRow dr) { var index = _pagePanels.Count(); var panel = new FlowLayoutPanel { Dock = DockStyle.Fill, Location = new Point(0, 0), Visible = false }; panelBody.Controls.Add(panel); _pagePanels[index] = panel; #region Btns var query = _permissionDt.Rows.Cast<DataRow>(); var data = query.Where( x => int.Parse(x[RemoteService.PermissionService.ParentId].ToString()) == int.Parse(dr[RemoteService.PermissionService.ModuleId].ToString())); if (!data.Any()) return index; int width = 80, height = 30; foreach (var dataRow in data) { var btn = new Button { Text = dataRow[RemoteService.PermissionService.ModuleName].ToString(), Size = new Size(width, height), Tag = dataRow[RemoteService.PermissionService.PermissioniNameSpace] }; btn.Click += btnPage_Click; panel.Controls.Add(btn); } #endregion return index; }
> 利用點擊不同的元素展示相應的頁面
/// <summary> /// 顯示相應的頁面元素 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnDLL_Click(object sender, EventArgs e) { var index = int.Parse(((Button) sender).Tag.ToString()); foreach (KeyValuePair<int, FlowLayoutPanel> flowLayoutPanel in _pagePanels) { flowLayoutPanel.Value.Visible = flowLayoutPanel.Key == index; } } /// <summary> /// 打開相應的page /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void btnPage_Click(object sender, EventArgs e) { var name = ((Button) sender).Tag.ToString(); var form = GetModule(name) as Form; form.ShowDialog(); } #region /// <summary> /// 利用反射去加載相應的頁面 /// </summary> /// <param name="path"></param> /// <param name="mainNamespace"></param> /// <returns></returns> private object GetModule(string path, string mainNamespace = "ReflectionPermissionDemo") { var curNamespace = ""; var index = path.IndexOf('.'); if (index > -1) { curNamespace = "." + path.Substring(0, index); } else { curNamespace = ""; } var assemblyPath = mainNamespace + curNamespace; var classPath = mainNamespace + "." + path; object module = null; if (_formTypes.ContainsKey(classPath)) { module = Activator.CreateInstance(_formTypes[classPath]); } else { try { module = Assembly.Load(assemblyPath).CreateInstance(classPath); if (module != null) _formTypes.Add(classPath, module.GetType()); } catch { // 查找當前已加載的dll。 Type type = null; foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies()) { if (!assembly.FullName.Contains(mainNamespace)) continue; type = assembly.GetType(classPath, false); if (type != null) { break; } } if (type == null) { throw; } else { module = Activator.CreateInstance(type); if (module != null) { _formTypes.Add(classPath, type); } ; } } } return module; } #endregion
跑一下,看一下效果
源碼 : ReflectionPermissionDemo.zip
github 地址: https://github.com/lpxxn/ReflectionPermissionDemo