本篇將要和大家分享的是一個簡單的後臺管理系統,這裡先發個地址http://www.lovexins.com:8081/(登陸賬號:youke,密碼:123123;高階使用者賬號:gaoji,密碼:123123)有興趣的各位可以先簡單看下效果,此係統採用:Ace的h5樣式+Mvc5.0 + redis+sqlserver+shenniu.pager.js構建完成,構建此專案初衷為了有一套自己現成的h5後臺系統,為了以後能快速開發搭建一些系統;專案原始碼暫時不開源(完善後開源),如果您的確認可或者想研究下,可以掃部落格下方二維碼支援一下獲取原始碼(哪怕1分錢都可),當然此文章主要目的不是為了廣告,而是為了分享一些重要或者常用的程式碼處理方式,希望大家喜歡,多多支援:
. Controller中使用自帶生成的Dispose(bool)好與壞
. List集合生成許可權樹
. 對比集合,載入checkboxlist
. 為啥使用redis來儲存session
下面一步一個腳印的來分享:
. Controller中使用自帶生成的Dispose(bool)好與壞
首先,咋們先來看下mvc模板自動生成的Dispose重寫方法:
1 protected override void Dispose(bool disposing) 2 { 3 if (disposing) 4 { 5 db.Dispose(); 6 } 7 base.Dispose(disposing); 8 }
因為Controller實現了介面IDisposable,所以裡面可以使用Dispose方法,再看db.dispose()這是用來釋放連線資料庫物件的,如果細心的朋友可以在除錯的時候發現,我們在執行某個連線資料庫操作後,退出action的時候會進入這個重新的Dispose方法中去,主要目的用來釋放在Controller最上面生成的連線資料庫物件,我這裡是:
看到這裡個人覺吧這個物件放在這使用起來挺方便的,不過帶來一個問題就是,如果在業務邏輯複雜的地方,直接使用這個db,直到最後執行完整個action退出Controller的時候才dispose釋放連線會不會有問題,心中存疑,再加上前幾天看了一篇博文,分享的內容大概有如此的字樣:ef不需要使用using(原理也就是dispose)自己就能在操作完資料庫後釋放,原因底層已經實現了這個dispose;如此更讓人感覺直接在Controller類下面申明資料庫連結方便的很,但試為什麼微軟mvc模板Controller中會再自帶一個dispose方法呢,這兩種情況存在不排除的說法,當然今天的主要目的不是為了驗證這兩種到底哪個是對的,此文章暫時以自動生成的dispose為合理說法來分享內容(注:朋友們請勿摳字眼);
一起來看,假如自動模板生成的全部靠Controller重寫的這個dispose來釋放資料連線,那麼在多業務邏輯下,比如我在登陸的時候我們除了匹配唯一使用者外,還需要記錄獲取許可權樹,加入redis的session儲存其中等操作,不可能等這些操作完了再來釋放資料連線,這樣登陸使用者多了一定會有問題吧,如果是這種情況的問題,或許只能通過在某處運算元據庫後即時釋放連線了;以上是格式觀點和看法,或許有不妥之處,請聯絡指正,謝謝,特別是using真的不需要嗎這個問題;
. List集合生成許可權樹
首先,我這裡定義了一個固定的實體:
/// <summary> /// 選單類 /// </summary> public class MoMenu { /// <summary> /// 選單Id (必填) /// </summary> public int Id { get; set; } /// <summary> /// 名稱 /// </summary> public string Name { get; set; } /// <summary> /// 連結地址 (建議填寫,每個需要登入訪問的Action需要使用這個對比是否有許可權) /// </summary> public string Link { get; set; } /// <summary> /// 描述 /// </summary> public string Des { get; set; } /// <summary> /// 圖示樣式,對應StageClass.MoIcon /// </summary> public string Icon { get; set; } /// <summary> /// 排序(升序) /// </summary> public int Sort { get; set; } /// <summary> /// 父級選單Id,最頂層父級預設0 /// </summary> public int ParentId { get; set; } /// <summary> /// 是否導航欄 /// </summary> public bool IsMenu { get; set; } /// <summary> /// 選單子級集合 /// </summary> public List<MoMenu> ListMenu { get; set; } }
主要用來裝系統中所有選單的資料,並且區分層級關係,然後通過如下方法:
1 /// <summary> 2 /// List集合生成選單樹 3 /// </summary> 4 /// <typeparam name="T">對比選中選單的物件</typeparam> 5 /// <param name="html"></param> 6 /// <param name="list">系統全部選單(需要有層級關係)</param> 7 /// <param name="name">生成的checkboxlist的name</param> 8 /// <param name="checkList">選中選單的集合</param> 9 /// <param name="defValFiled">選中匹配的預設值</param> 10 /// <param name="isEnable">是否啟用(檢視狀態不需要啟用)</param> 11 /// <param name="nLoop">迴圈層次(可能多餘的吧)</param> 12 /// <returns></returns> 13 public static MvcHtmlString CheckBoxMenuByList<T>( 14 this HtmlHelper html, 15 List<StageModel.MoMenu> list, 16 string name = "RadStatus", 17 IEnumerable<T> checkList = null, 18 string defValFiled = "Id", 19 20 bool isEnable = true, 21 int nLoop = 0) 22 where T : class ,new() 23 { 24 var sbHtml = new StringBuilder(string.Empty); 25 sbHtml.AppendFormat("<ul class='divmenu' style=\"list-style:none;{0}\">", nLoop <= 0 ? "display:block" : "display:none"); 26 foreach (var item in list) 27 { 28 var isCheck = false; 29 if (checkList != null) 30 { 31 foreach (var checkItem in checkList) 32 { 33 var ty = checkItem.GetType(); 34 var val = ty.GetProperty(defValFiled).GetValue(checkItem, null); 35 isCheck = val.ToString().Equals(item.Id.ToString()); 36 if (isCheck) { break; } 37 } 38 } 39 40 sbHtml.Append("<li>"); 41 sbHtml.AppendFormat("<input id=\"{0}{1}\" name=\"{0}\" type=\"checkbox\" value=\"{1}\" {3} {4}/><label>{2}<b>{5}</b></label>", // class=\"arrow fa fa-angle-down\" 42 name, 43 item.Id, 44 item.Name, 45 isCheck ? "checked=\"checked\"" : "", 46 isEnable ? "" : "disabled=\"disabled\"", 47 48 item.ListMenu == null ? "" : (item.ListMenu.Count > 0 ? string.Format("[{0}]", item.ListMenu.Count) : "") 49 ); 50 if (item.ListMenu == null) { sbHtml.Append("</li>"); continue; } 51 if (item.ListMenu.Count > 0) 52 { 53 sbHtml.Append(CheckBoxMenuByList(html, item.ListMenu, name, checkList, defValFiled, isEnable, isCheck ? 0 : nLoop++)); 54 } 55 sbHtml.Append("</li>"); 56 } 57 sbHtml.Append("</ul>"); 58 return MvcHtmlString.Create(sbHtml.ToString()); 59 }
遍歷生成選單樹,功能有:1.在檢視狀態即可使其禁用選擇,2.編輯狀態匹配物件選中選單;然後需要在試圖中增加如js程式碼:
1 $("ul[class='divmenu'] li input[type='checkbox']").on("click", function () { 2 3 var isCheck = $(this).is(":checked"); 4 //子級 5 $(this).nextAll("ul").find("li input[type='checkbox']").prop("checked", isCheck); 6 }); 7 $("ul[class='divmenu'] li label").on("click", function () { 8 $(this).next("ul[class='divmenu']").toggle("normal"); 9 });
最後,只需要在需要用到該選單樹的地方新增程式碼如: @Html.CheckBoxMenuByList(Stage.Com.Extend.StageClass.GetAllMenus(), "MenuIds", Model.MoRoleAndMenus, "MenuId") 即可,看到的效果圖:
具體大家可以登陸ShenNiu.MVC試試效果
. 對比集合,載入checkboxlist
首先,直接貼程式碼如:
1 /// <summary> 2 /// 對比集合,載入checkboxlist 3 /// </summary> 4 /// <typeparam name="T">目標物件</typeparam> 5 /// <param name="html"></param> 6 /// <param name="orgList">目標集合</param> 7 /// <param name="orgFiledVal">目標對應checkbox的value值的屬性名稱</param> 8 /// <param name="orgFiledText">對應的checkbox文字的text值的屬性名稱</param> 9 /// <param name="destList">匹配集合</param> 10 /// <param name="destFiled">匹配列的屬性名稱</param> 11 /// <param name="sClass">樣式</param> 12 /// <param name="name">checkboxlist的Name</param> 13 /// <returns></returns> 14 public static MvcHtmlString CheckBoxRoleByList<T, TT>( 15 this HtmlHelper html, 16 IEnumerable<T> orgList, 17 string orgFiledVal, 18 string orgFiledText, 19 20 IEnumerable<TT> destList = null, 21 string destFiled = "", 22 23 string sClass = "", 24 string name = "cbAll") 25 where T : class ,new() 26 where TT : class,new() 27 { 28 var sbHtml = new StringBuilder(string.Empty); 29 if (orgList.Count() <= 0) { return MvcHtmlString.Create(string.Empty); } 30 31 sbHtml.AppendFormat("<div class=\"{0}\">", sClass); 32 foreach (var item in orgList) 33 { 34 var ty = item.GetType(); 35 var val = ty.GetProperty(orgFiledVal).GetValue(item, null); 36 var text = ty.GetProperty(orgFiledText).GetValue(item, null); 37 38 var isMatch = false; 39 if (destList != null) 40 { 41 foreach (var destItem in destList) 42 { 43 var destty = destItem.GetType(); 44 var destval = destty.GetProperty(destFiled).GetValue(destItem, null); 45 46 if (val.ToString().ToUpper().Equals(destval.ToString().ToUpper())) 47 { 48 isMatch = true; 49 break; 50 } 51 } 52 } 53 sbHtml.AppendFormat("<label><input type='checkbox' name='{0}' value='{1}' {3} />{2}</label>", name, val, text, isMatch ? "checked='checked'" : ""); 54 } 55 sbHtml.Append("</div>"); 56 return MvcHtmlString.Create(sbHtml.ToString()); 57 }
使用編輯頁面的效果圖如:
看到效果是預設繫結了該使用者對應的許可權“高階使用者”,程式碼需要重點是:
1 var ty = item.GetType(); 2 var val = ty.GetProperty(orgFiledVal).GetValue(item, null); 3 var text = ty.GetProperty(orgFiledText).GetValue(item, null);
主要作用就是指定特定的屬性,獲取特定屬性的value值;item是List集合中的某個物件,具體引數說明可以看下程式碼備註;
. 為啥使用redis來儲存session
這個小標題不好定義,我個人的見解,大家可以看看罷了;1:redis儲存資料可以設定失效時間,這就類似於session失效的效果一樣,所以redis能很好的來當做session容器;2:用上面一點我們可以再增加nginx等分散式服務,以此來打架一個分散式架構,這樣在更新子系統的時候不會造成所以的業務癱瘓,可以不對使用者的操作造成阻礙,彷彿沒有更細過一樣;3:基於redis搭建session伺服器後session伺服器這個時候可以單獨分到另外一臺伺服器上,這樣減少了記憶體佔有率,應用程式和session分佈多個伺服器,大大提高承載率,不至於說什麼千萬級別就把您系統cpu爆滿了,session還各種丟失的情況;4.使用redis打架服務後可以利用剩餘空間儲存一些不長變動的資料和訊息,列如:系統中的選單欄,一些訊息提醒,郵件傳送等資料,都可以使用其儲存;
以上是個人的觀點和總結,希望能對大家有幫助;由於本人經驗有限,如有不合理的地方請多多指正,謝謝。