從零開始編寫自己的C#框架(19)——Web層后端權限模塊


  不知不覺本系統寫了快三個月了,最近寫頁面的具體功能時感覺到有點吃力,很多地方如果張嘴來講的話可以說得很細,很全面,可寫成文字的話,就不太會寫了,有些地方想講得清晰的話,得用多幾倍的文字+實例+變化中的圖片才能表達得清楚,而寫這些又太費時間了,近段時間又特忙,所以只能是盡力而為,希望大家自行研究,如果有什么地方不明白的,發發評論或郵件給我,我再重新詳細講解。

 

  說回正題,對於頁面訪問權限以及每個按鍵的權限控制,很久以前用過好幾種不同的方法,比如為每個控件分配名稱或編碼,然后在寫代碼時綁定這些值,又比如用XML來配置制權限等方法。這些方法都比較麻煩,而且由於都是使用編碼方法,開發時需要一個個進行綁定,容易出錯。經過后來不斷的完善,最后完成了本系統所采用的頁面控件注冊管理來綁定控件權限(如有雷同,純屬巧合,哈哈...),首先創建菜單,並綁定好對應的文件(頁面),然后將系統要用到的名稱添加到公用標識庫中,跟着在頁面控件權限管理頁面對各個頁面控件進行綁定(只需要點擊鼠標即可),通過職位(角色)來賦於不同的操作權限,只需要設置好管理員的職位,那么該管理員就擁有他所綁定的角色的全部權限了。

  如下面的說明

  首先在后端注冊菜單(菜單綁定頁面)

 

  創建公用頁面權限標識,待用

  為需要綁定頁面控件權限的菜單頁面綁定操作控件(左列為上圖錄入的公共控件名稱,右列為已綁定的頁面控件,只需要點擊鼠標就可以輕松綁定)

   創建好部門

  在不同部門創建相應的職位(角色)

  為不同角色設置菜單與頁面控件操作權限

 

  開發說明:(主要講講與上一章中不同的內容)

  1、PagePowerSignPublicList.aspx.cs 公用頁面控件權限標識列表管理文件

  這個頁面是比較經典的普通列表頁面,比較代碼,有以下一些地方和菜單頁面不一樣的

 1         #region 加載數據
 2         /// <summary>讀取數據</summary>
 3         public override void LoadData()
 4         {
 5             //設置排序
 6             if (sortList == null)
 7             {
 8                 Sort(null);
 9             }
10 
11             //綁定Grid表格
12             bll.BindGrid(Grid1, Grid1.PageIndex + 1, Grid1.PageSize, null, sortList);
13         }
14 
15         #endregion

  對於排序函數的調用,菜單頁面是有層次感列表,所以要自定義排序函數,而對於正常的列表,我們直接調用父類的Sort(null)函數即可

  綁定Grid表格也與菜單頁面調用的不一樣,bll.BindGrid()函數使用的是參數中需要添加當前在第幾頁,每個頁面顯示多少行,null是條件參數,后面會詳細解釋它怎么使用,最后一個參數是排序規則參數

  另外,Delete()刪除函數也使用批量刪除功能,不過會在刪除前進行檢查,發現指定記錄不能刪除時,則彈出提示是那個Id的記錄無法刪除,大家自行比較一下兩個列表文件就明白了。

 

  2、PagePowerSignPublicEdit.aspx.cs公用頁面控件權限標識編輯文件

  這個文件要注意的地方是Save()函數

 1         #region 保存
 2         /// <summary>
 3         /// 數據保存
 4         /// </summary>
 5         /// <returns></returns>
 6         public override string Save()
 7         {
 8             string result = string.Empty;
 9             int id = ConvertHelper.Cint0(hidId.Text);
10 
11             try
12             {
13                 #region 數據驗證
14 
15                 if (string.IsNullOrEmpty(txtCName.Text.Trim()))
16                 {
17                     return txtCName.Label + "不能為空!";
18                 }
19                 var sName = StringHelper.Left(txtCName.Text, 20);
20                 if (PagePowerSignPublicBll.GetInstence().Exist(x => x.CName == sName && x.Id != id))
21                 {
22                     return txtCName.Label + "已存在!請重新輸入!";
23                 }
24                 if (string.IsNullOrEmpty(txtEName.Text.Trim()))
25                 {
26                     return txtEName.Label + "不能為空!";
27                 }
28                 var sEname = StringHelper.Left(txtEName.Text, 50);
29                 if (PagePowerSignPublicBll.GetInstence().Exist(x => x.EName == sEname && x.Id != id))
30                 {
31                     return txtEName.Label + "已存在!請重新輸入!";
32                 }
33 
34                 #endregion
35 
36                 #region 賦值
37                 //定義是否更新標識——即當前記錄的名稱是否改變了
38                 bool isUpdate = false;
39 
40                 //獲取實體
41                 var model = new PagePowerSignPublic(x => x.Id == id);
42 
43                 //判斷是否有改變名稱
44                 if (id > 0 && (sName != model.CName || sEname != model.EName))
45                 {
46                     isUpdate = true;
47                 }
48 
49                 //設置名稱
50                 model.CName = sName;
51                 //設置英文名稱
52                 model.EName = sEname;
53                 #endregion
54 
55                 //----------------------------------------------------------
56                 //存儲到數據庫
57                 PagePowerSignPublicBll.GetInstence().Save(this, model);
58 
59                 //判斷是否需要同步更新關聯表字段
60                 if (isUpdate)
61                 {
62                     //調用更新函數,同步更新對應的所有記錄
63                     PagePowerSignBll.GetInstence().UpdateValue_For_PagePowerSignPublic_Id(this, model.Id, PagePowerSignTable.CName, model.CName, PagePowerSignTable.EName, model.EName);
64                 }
65             }
66             catch (Exception e)
67             {
68                 result = "保存失敗!";
69 
70                 //出現異常,保存出錯日志信息
71                 CommonBll.WriteLog(result, e);
72             }
73 
74             return result;
75         }
76         #endregion
View Code

  在函數中,大家可以找到下面這些代碼

 1   //定義是否更新標識——即當前記錄的名稱是否改變了
 2   bool isUpdate = false;
 3 
 4   //判斷是否有改變名稱
 5   if (id > 0 && (sName != model.CName || sEname != model.EName))
 6   {
 7     isUpdate = true;
 8   }
 9 
10   //判斷是否需要同步更新關聯表字段
11   if (isUpdate)
12   {
13     //調用更新函數,同步更新對應的所有記錄
14     PagePowerSignBll.GetInstence().UpdateValue_For_PagePowerSignPublic_Id(this, model.Id, PagePowerSignTable.CName, model.CName, PagePowerSignTable.EName, model.EName);
15   }

  這是用於我們修改A表記錄的名稱時,同步修改其他關聯表引用了這個記錄名稱字段的所有記錄

  為什么要這么處理呢?大家在看數據字典的數據庫結構時,會發現很多表與其他表關聯時,不單將其他表的Id引用了過來,還將這個Id對應的名稱也引用了,這種操作方式使我們在編寫查詢語句時,幾乎可以做到不用多表關聯,因為我們想要顯示的內容已經在查詢的表中存在了,這樣處理后,我們在修改相關表的名稱時,就必須同步修改關聯表中的名稱,對於這些關聯表名稱的修改,我們的T4模板也生成了相應的函數,如:UpdateValue_For_表名_Id()函數,然后直接按上面代碼編寫方法實現就可以了。

 

  3、UseLogList.aspx.cs用戶操作日志管理文件

  這個文件要注意的是InquiryCondition()函數

 1         /// <summary>
 2         /// 查詢條件
 3         /// </summary>
 4         /// <returns></returns>
 5         private List<ConditionHelper.SqlqueryCondition> InquiryCondition()
 6         {
 7             var wheres = new List<ConditionHelper.SqlqueryCondition>();
 8 
 9             //如果Id有值時,即表示查詢的是指定管理員的操作日志
10             if (_id != 0)
11             {
12                 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Where, LoginLogTable.Manager_Id, Comparison.Equals, _id));
13             }
14 
15             //起始時間
16             if (!string.IsNullOrEmpty(dpStart.Text.Trim()))
17             {
18                 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.AddDate, Comparison.GreaterOrEquals, StringHelper.FilterSql(dpStart.Text)));
19                 //終止時間
20                 if (!string.IsNullOrEmpty(dpEnd.Text.Trim()))
21                 {
22                     wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.AddDate, Comparison.LessOrEquals, StringHelper.FilterSql(dpEnd.Text)));
23                 }
24             }
25 
26             //ip地址
27             if (!string.IsNullOrEmpty(txtIp.Text.Trim()))
28             {
29                 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.Ip, Comparison.Equals, StringHelper.FilterSql(txtIp.Text)));
30             }
31             //登錄備注信息
32             if (!string.IsNullOrEmpty(txtloginfo.Text.Trim()))
33             {
34                 wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, LoginLogTable.Notes, Comparison.Like, "%" + StringHelper.FilterSql(txtloginfo.Text) + "%"));
35             }
36 
37             return wheres;
38         }

 

  這是查詢條件函數,ConditionHelper.SqlqueryCondition這個類是自定義封裝條件類

  它的構造函數(public SqlqueryCondition(ConstraintType ctype, string columnname, Comparison cparsion, object value, bool isParentheses = false))一共有5個參數,其中4個為必填參數

  第一個參數ConstraintType ctype為查詢的類型,主要是ConstraintType.Where、ConstraintType.And、ConstraintType.Or三種。

    如果有多個條件時,ConstraintType.Where只能放在最前面,且只能有一個條件使用這個參數,一般都很少用它。

    ConstraintType.And指的是當前參數與前面參數的關系是And關系

    ConstraintType.Or通常情況下指的是當前參數與前面參數的關系是Or關系

   第二個參數string columnname是條件列字段名稱

  第三個參數Comparison cparsion是表達式,使用Comparison.X來設置,根據需求設置==、>、>=、<、<=、like、in、not in......

  第四個參數object value是條件值,如果是in與not in表達式時,條件值必須為數據類型,比如:string[]、int[]、object[]等

  第五個參數bool isParentheses是加左括號,而右括號使用new ConditionHelper.SqlqueryCondition()或new ConditionHelper.SqlqueryCondition(Comparison.CloseParentheses)來添加,然后in或not in表達式時,必須緊跟着右括號

 

  例子1:

  (A<2 Or A>= 10) And B=100

1   var wheres = new List<ConditionHelper.SqlqueryCondition>();
2 
3   wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Where, "A", Comparison.LessThan, 2, true));
4 
5   wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Or, "A", Comparison.GreaterOrEquals, 10));
6 
7   wheres.Add(new ConditionHelper.SqlqueryCondition());
8 
9   wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, "B", Comparison.Equals, 100));

  例子2:

  A == 2 And B in arr And C like 'abc%' (in查詢必須加左右括號)

1   var wheres = new List<ConditionHelper.SqlqueryCondition>();
2 
3   wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.Where, "A", Comparison.Equals, 2));
4   //加左括號
5   wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, "B", Comparison.In, arr, true));
6   //加右括號
7   wheres.Add(new ConditionHelper.SqlqueryCondition());
8 
9   wheres.Add(new ConditionHelper.SqlqueryCondition(ConstraintType.And, "C", Comparison.Like, "abc%"));

   這個封裝類最終會生成SubSonic3.0底層調用的條件參數,經過測試發現,SubSonic3.0底層調用的條件參數只支持單括號,不支持括號的嵌套,多重嵌套后最終生成的也只是單括號且會出錯,所以多重嵌套括號時,最好使用其它方法來實現,比如Linq、存儲過程、SQL語句拼接等。

 

  4、小結

  本次更新的代碼功能比如多,已將部門、職位、公用頁面控件權限標識、頁面控件權限管理、在線用戶列表、用戶登陸日志、用戶操作日志、錯誤日志等功能都已完成了,同時也對在線用戶相關類和函數進行了優化處理。

  內容看起來好像挺多了,其實都是對之前所寫的模板函數以及其他公共函數的調用,這么多頁面查看cs代碼時可以發現,里面的內容都差不多,都是很格式化的東西,如果你嘗試過用這些已完成的模板頁面添加新功能,就可以發現很簡單,開發也很迅速。

  至於每個頁面如何去實現,接下來就不必再像之前那樣講得很細致了,會針對一些特殊功能或函數的調用進行解說,往細講也只能從邏輯層的各個函數應用來說起,所以就不再羅嗦一一細說,代碼中有大量的注釋,大家慢慢研究吧。

 

  最好附上幾張頁面的效果圖

 

 

由於框架不是非常成熟,很多朋友不是用來學習而是直接用到項目中,但不熟悉框架引起不少小問題,所以停止提供下載,有需要學習的可以到群共享里下,不便之處敬請諒解。

 

 

 

 版權聲明:

  本文由AllEmpty原創並發布於博客園,歡迎轉載,未經本人同意必須保留此段聲明,且在文章頁面明顯位置給出原文鏈接,否則保留追究法律責任的權利。如有問題,可以通過1654937@qq.com 聯系我,非常感謝。

 

  發表本編內容,只要主為了和大家共同學習共同進步,有興趣的朋友可以加加Q群:327360708 ,大家一起探討。

 

  更多內容,敬請觀注博客:http://www.cnblogs.com/EmptyFS/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM