ASP.NET MVC+EF框架+EasyUI實現權限管系列
(開篇) (1):框架搭建 (2):數據庫訪問層的設計Demo (3):面向接口編程 (4 ):業務邏輯層的封裝
(5):前台Jquery easyUI實現 (6):EF上下文實例管理
前言:上篇博客中我們重新對EF框架實現上下文進行了重新的操作,而且我們也建立了DbSession,使用CallContext類幫我們返回當前線程內唯一的數據庫上下文,這樣的話我們就在基倉儲里面再也不用new實例話,只需要使用簡單工廠來調用EFContextFactory里面實現線程內唯一的方法:GetCurrentDbContext,那么這篇博客我們繼續來說DbSession類的封裝,此系列已經寫到了7了,我在這里建議大家在閱讀的時候能夠全部閱讀,或者你本來明白這種模式的話就不需要了,那么DbSession類究竟有什么神奇之處,請看下面的介紹。
1. EFContextFactory線程內唯一復習
(1)在這里我們再解釋一下線程內唯一,拿用老師的說法就是,假設這個線程就是衣服,而每件衣服上面都有口袋,那么我們就可以把這個口袋看成數據槽(CallContext指向的那一塊內存空間),每件衣服都會有自己的口袋(特殊的除外),而這時候你要從衣服口袋里面拿出去一個糖,這時候我要先去口袋里面檢查檢查有沒有,如果衣服口袋里面有我們就拿出來,負責我先將糖放到衣服口袋里,你下次來的時候繼續在判斷有沒有。而且每件衣服都有自己獨立的口袋,他們是沒有任何關聯的,着就保證了每件衣服內都會有糖(線程內唯一)。
(2)CallContext可以幫助我們保證線程內唯一。
2.DbSession
(1)在DbSession中封裝了我們所有倉儲的屬性,在屬性里面我們能夠拿到我們倉儲的實例,那么DbSession可以看成是我們整個數據庫訪問層的統一入口,另外在DbSession里面我們有封裝了一個SaveChanges方法,那么在SaveChanges里面怎么做呢?請看下面的代碼:
namespace LYZJ.UserLimitMVC.DAL { //一次跟數據庫交互的會話 public class DbSession //代表應用程序跟數據庫之間的一次會話,也是數據庫訪問層的統一入口 { public IDAL.IRoleRepository RoleRepository { get { return new RoleRepository(); } } public IDAL.IUserInfoRepository UserInfoRepository { get { return new UserInfoRepository(); } } //代表:當前應用程序跟數據庫的繪畫內所有的實體的變化,更新會數據庫 public int SaveChanges() { //調用EF上下文的SaveChanges方法 return DAL.EFContextFactory.GetCurrentDbContext().SaveChanges(); } } }
(2)我們封裝了一個SaveChanges方法的話,它就直接去獲取當前線程里面的上下文,然后調用上下文的SaveChanges方法,就相當於直接把當前線程內部所有實體的改變提交到數據庫里面,看上面的代碼可能大部人都沒有什么感覺,這是干什么呢?這時候我在作一個工作,就是將BaseRepository(倉儲)中增刪改方法的db.SaveChanges()刪除掉,這時候我們就發現好處了吧,那就是我們數據庫訪問層雖然調用的方法沒有真正的保存到數據庫里面去,也就是把SaveChanges全部給放到DbSession中去實現了,那么DbSession我們就能夠看成一個真正的會話了。
(3)也就是說我們在前面調用了很多次的增刪改的實體之后(操作很多表),而只需要去DbSession中調用一個SaveChanges方法,就可以把所有的表實體的變化都放到數據庫中去。
3.將SaveChanges方法放到DbSession中好處
(1)那么我們將SaveChanges方法放到DbSession中有什么好處呢?我們再項目中舉個例子來說:我們回到BaseService里面拿添加倉儲來說,假設我們添加實體這里,我們再上面做了一個添加用戶的實體,然后我們業務里面還有修改一個狀態,那么我們的代碼如下所示:
1 //實現對數據庫的添加功能 2 3 public T AddEntity(T entity) 4 5 { 6 7 //調用T對應的倉儲來做添加工作 8 9 CurrentRepository.AddEntity(entity); 10 11 CurrentRepository.UpdateEntity(entity); 12 13 }
(2)在這里我們一個業務場景可能會操作很多個表,在之前的做法中我們所有的增刪改方法每調用一次就會執行SaveChanges一次,比如添加用戶當我們添加用戶就要SaveChangers一次,然后付給權限有需要一次,那這樣的話就跟數據庫交互了很多次,那么我們就才想到將SaveChangers方法提取到DbSession中去。
(3)如果我們將SaveChangers提取到DbSession中去的話,那么當我們以后操作多個實體之后直接來調用一下DbSession實現,這時候對應的上面的添加的方法即可這樣實現:
1 //DbSession的存放 2 3 public DbSession _DbSession = new DbSession(); 4 5 //基類的構造函數 6 7 public BaseService() 8 9 { 10 11 SetCurrentRepository(); //構造函數里面去調用了,此設置當前倉儲的抽象方法 12 13 } 14 15 public abstract void SetCurrentRepository(); //子類必須實現 16 17 //實現對數據庫的添加功能 18 19 public T AddEntity(T entity) 20 21 { 22 23 //調用T對應的倉儲來做添加工作 24 25 CurrentRepository.AddEntity(entity); 26 27 CurrentRepository.UpdateEntity(entity); 28 29 _DbSession.SaveChanges(); 30 31 }
(4)這時候我們將DbSession封裝的話,這樣就使的DbSession非常靈活,就是把SaveChangers的權利從數據庫訪問層提高到了業務邏輯層,讓業務邏輯層來控制SaveChangers方法,而數據庫訪問層不需要進行SaveChangers方法了,那么就可以保證在一個業務場景中操作多個表只需要一次的提交,減少了跟數據庫交互的次數。
4.對BaseService的修改
(1)通過上面的介紹,下面我們來對BaseService進行修改,修改的最終代碼如下所示:
1 namespace LYZJ.UserLimitMVC.BLL 2 3 { 4 5 public abstract class BaseService<T> where T : class, new() 6 7 { 8 9 //當前倉儲 10 11 public IDAL.IBaseRepository<T> CurrentRepository { get; set; } 12 13 //DbSession的存放 14 15 public DbSession _DbSession = new DbSession(); 16 17 //基類的構造函數 18 19 public BaseService() 20 21 { 22 23 SetCurrentRepository(); //構造函數里面去調用了,此設置當前倉儲的抽象方法 24 25 } 26 27 public abstract void SetCurrentRepository(); //子類必須實現 28 29 //實現對數據庫的添加功能 30 31 public T AddEntity(T entity) 32 33 { 34 35 //調用T對應的倉儲來做添加工作 36 37 var AddEntity = CurrentRepository.AddEntity(entity); 38 39 _DbSession.SaveChanges(); 40 41 return AddEntity; 42 43 } 44 //實現對數據的修改功能 45 46 public bool UpdateEntity(T entity) 47 48 { 49 50 CurrentRepository.UpdateEntity(entity); 51 52 return _DbSession.SaveChanges() > 0; 53 54 } 55 //實現對數據庫的刪除功能 56 57 public bool DeleteEntity(T entity) 58 59 { 60 61 CurrentRepository.DeleteEntity(entity); 62 63 return _DbSession.SaveChanges() > 0; 64 65 } 66 //實現對數據庫的查詢 --簡單查詢 67 68 public IQueryable<T> LoadEntities(Func<T, bool> whereLambda) 69 70 { 71 72 return CurrentRepository.LoadEntities(whereLambda); 73 74 } 75 76 /// <summary> 77 78 /// 實現對數據的分頁查詢 79 80 /// </summary> 81 82 /// <typeparam name="S">按照某個類進行排序</typeparam> 83 84 /// <param name="pageIndex">當前第幾頁</param> 85 86 /// <param name="pageSize">一頁顯示多少條數據</param> 87 88 /// <param name="total">總條數</param> 89 90 /// <param name="whereLambda">取得排序的條件</param> 91 92 /// <param name="isAsc">如何排序,根據倒敘還是升序</param> 93 94 /// <param name="orderByLambda">根據那個字段進行排序</param> 95 96 /// <returns></returns> 97 98 public IQueryable<T> LoadPageEntities<S>(int pageIndex, int pageSize, out int total, Func<T, bool> whereLambda, 99 100 bool isAsc, Func<T, S> orderByLambda) 101 102 { 103 104 return CurrentRepository.LoadPageEntities(pageIndex, pageSize, out total, whereLambda, isAsc, orderByLambda); 105 106 } 107 108 } 109 110 }
(2) 雖然DbSession封裝的很簡單,但是它兼顧了簡單工廠模式和SaveChangers方法(當前會話比較重要的功能), 雖然SaveChangers方法簡單的幾行代碼,但是我們在這里實現了一個模式,那就是單元工作模式(UintWork)。
(3) 單元工作模式,就是批量的把對數據庫的操作提交到數據庫中去,就是把一系列對數據庫的操作封裝成一個單元工作,一次性的把單元工作里面的所有改變都提交到數據庫里面去,這就是單元工作模式,它的目的就是為了提高跟數據庫交互的效率,減少跟數據庫交互的次數。
5.小結
(1)這篇博客在最后我要給出一些博友們在前面給我留言的回答,首先是我寫的是權限管理嗎?我的回答肯定是的,但是你前面還沒有看到那里,那是因為我還在寫底層的信息,因為當我們寫好底層的時候,那么界面的東西和業務邏輯還不好些嗎。
(2)我現在這個項目做的有點復雜,很多人說沒有實用價值,我這里要說的也是,我在這篇博客的開頭我就說了這個系列的博客不是教大家怎么去做項目,而是說思想,而且我相信,只要我們理解了面向對象的思想,很多問題都會迎刃而解。
(3)最后感謝廣大博客閱讀我的博客,謝謝大家的支持,你們的支持就是我的動力。
源碼下載
(1):完整源碼下載
Kencery返回本系列開篇