本篇將要和大家分享的是一個簡單的后台管理系統,這里先發個地址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打架服務后可以利用剩余空間存儲一些不長變動的數據和消息,列如:系統中的菜單欄,一些消息提醒,郵件發送等數據,都可以使用其保存;
以上是個人的觀點和總結,希望能對大家有幫助;由於本人經驗有限,如有不合理的地方請多多指正,謝謝。
