前一段時間,寫了一步一步asp.net的一系列博客,最近,也快要大四,忙着准備找個工作,這也算是最后一個假期了,這個系列可能不太長,盡量寫完.還是多學習,少扯淡的風格,我們的學習還好
繼續,現在開始學習asp.net MVC系列,基礎知識,大家看博客園相關的一系列就可以了,我們在
這里學一下一個權限管理的設計.我采用的是Asp.net MVC+EF+N層的方式,順便加入點
spring.net注入的部分,當然我們最主要的還是關於權限設計的部分.而架構,咱也沒學過太復
雜的架構,我們還是從最常用的三層架構進行擴展.
參考書籍:
<<重構,改善既有代碼的設計>>
<<.net應用架構設計原則,模式與實踐>>
<<.net設計范式>>
<<代碼整潔之道>>
以及網上眾多的教程,博客等等
首先,我們要講解一下關於項目的搭建部分.我們的項目主要分為5部分,業務邏輯層,數據訪問層,界面層,領域模型層以及
一個公共類的部分.
當然,我們要先建立一個工程,並建立相應的解決方案文件夾,並且保持清晰程度,我們在工程加上前綴(最好是公司前綴)后面加上部分.
然后在每個解決方案文件夾下面建立響應的項目,
這里主要是:
業務邏輯層:
TZHSWEET.IBLL:業務邏輯層接口
TZHSWEET.BLL:業務邏輯層實現
數據訪問層:
TZHSWEET.IDao:數據訪問層接口
TZHSWEET.Dao:數據訪問層實現
領域模型層:
TZHSWEET.Entity:這是EF建立的模型
TZHSWEET.ViewModel:這個是用來傳遞UI層和業務邏輯層的相關模型對象
界面層:
TZHSWEET.WebUI:主要的MVC和LigerUI實現的界面部分
TZHSWEET.UI:關於MVC公共UI定義的部分
公共類庫部分:
TZHSWEET.CacheStorage主要是常用的緩存
TZHSWEET.Common主要是一些公共類實現等等
這里僅僅是三層架構按照我自己的想法做了一個適應性擴展,呵呵.
EF不用說了,這里只是一個簡單的應用
在這里我先講解關於Idao和Dao部分
我們的目標是"0"增刪改查的數據訪問層實現,
主要是靠EF的定義通用的增刪改查,然后其他類繼承增刪改查接口和相應的自定義子類接口,實現擴展.
首先,我們從以前寫代碼的經驗知道,我們的Dao主要是做增刪改查等方面,我們就先定義一個公共的接口,叫做IBaseDao,這個接口定義泛型的增刪改查,
1: /* 作者: tianzh
2: * 創建時間: 2012/7/16 11:01:34
3: *
4: */
5: using System;
6: using System.Collections.Generic;
7: using System.Linq;
8: using System.Text;
9: using System.Data.Objects;
10:
11: namespace TZHSWEET.IDao
12: {
13: public interface IBaseDao<T>// where T:class //限制class
14: {
15: #region 查詢普通實現方案(基於Lambda表達式的Where查詢)
16: /// <summary>
17: /// 獲取所有Entity
18: /// </summary>
19: /// <param name="exp">Lambda條件的where</param>
20: /// <returns></returns>
21: IEnumerable<T> GetEntities(Func<T, bool> exp);
22:
23: /// <summary>
24: /// 計算總個數(分頁)
25: /// </summary>
26: /// <param name="exp">Lambda條件的where</param>
27: /// <returns></returns>
28: int GetEntitiesCount(Func<T, bool> exp);
29:
30: /// <summary>
31: /// 分頁查詢(Linq分頁方式)
32: /// </summary>
33: /// <param name="pageNumber">當前頁</param>
34: /// <param name="pageSize">頁碼</param>
35: /// <param name="orderName">lambda排序名稱</param>
36: /// <param name="sortOrder">排序(升序or降序)</param>
37: /// <param name="exp">lambda查詢條件where</param>
38: /// <returns></returns>
39: IEnumerable<T> GetEntitiesForPaging(int pageNumber, int pageSize, Func<T, string> orderName, string sortOrder, Func<T, bool> exp);
40:
41: /// <summary>
42: /// 根據條件查找
43: /// </summary>
44: /// <param name="exp">lambda查詢條件where</param>
45: /// <returns></returns>
46: T GetEntity(Func<T, bool> exp);
47:
48: #endregion
49: #region 查詢Sql語句外接接口的查詢實現
50: /// <summary>
51: /// 獲取所有Entity(立即執行請使用ToList()
52: /// </summary>
53: /// <param name="CommandText">Sql語句</param>
54: /// <param name="objParams">可變參數</param>
55: /// <returns></returns>
56: IEnumerable<T> GetEntities(string CommandText);
57:
58: /// <summary>
59: /// 計算總個數(分頁)
60: /// </summary>
61: /// <param name="CommandText">Sql語句</param>
62: /// <param name="objParams">可變參數</param>
63: /// <returns></returns>
64: int GetEntitiesCount(string CommandText);
65:
66: /// <summary>
67: /// 分頁查詢(Linq分頁方式)
68: /// </summary>
69: /// <param name="tableName">表名</param>
70: /// <param name="pageNumber">當前頁</param>
71: /// <param name="pageSize">頁碼</param>
72: /// <param name="orderName">lambda排序名稱</param>
73: /// <param name="sortOrder">排序(升序or降序)</param>
74: /// <param name="CommandText">Sql語句</param>
75: /// <param name="Count">總個數</param>
76: /// <returns></returns>
77: IEnumerable<T> GetEntitiesForPaging(string tableName, int pageNumber, int pageSize, string orderName, string sortOrder, string CommandText, out int Count);
78:
79: /// <summary>
80: /// 根據條件查找
81: /// </summary>
82: /// <param name="CommandText">Sql語句</param>
83: /// <param name="objParams">可變參數</param>
84: /// <returns></returns>
85: T GetEntity(string CommandText);
86:
87: #endregion
88: /// <summary>
89: /// 插入Entity
90: /// </summary>
91: /// <param name="model"></param>
92: /// <returns></returns>
93: bool Insert(T entity);
94: /// <summary>
95: /// 更新Entity
96: /// </summary>
97: /// <param name="model"></param>
98: /// <returns></returns>
99: bool Update(T entity);
100: /// <summary>
101: /// 刪除Entity
102: /// </summary>
103: /// <param name="entity"></param>
104: /// <returns></returns>
105: bool Delete(T entity);
106: }
107: }
接下來,我們需要定義一系列的接口繼承這個接口,代碼類似:
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using TZHSWEET.Entity;
6:
7: namespace TZHSWEET.IDao
8: {
9:
10: public interface IDepartmentDao<T> : IBaseDao<T> where T : class
11: {
12:
13: }
14: }
這層接口,大家可能認為沒什么必要,但是,這個接口非常重要,這個接口保證了我們用EF一個抽象類實現增刪改查的同時又增加了子類的自身擴展性.
接下來,就是Dao部分,我們需要很慎重的去設計,
首先我們要設計一個用EF實現的公共父類的BaseEFDao類,用他來實現增刪改查,
1: /* 作者: tianzh
2: * 創建時間: 2012/7/16 11:06:16
3: *
4: */
5: using System;
6: using System.Collections.Generic;
7: using System.Linq;
8: using System.Text;
9: using TZHSWEET.IDao;
10: using TZHSWEET.Entity;
11: using System.Data;
12: using TZHSWEET.Common;
13: using System.Data.Objects;
14: namespace TZHSWEET.EFDao
15: {
16: /// <summary>
17: /// 公共接口
18: /// </summary>
19: public class BaseEFDao<T> : IBaseDao<T> where T : class,new() //限制T為class
20: {
21:
22: #region 查詢普通實現方案(基於Lambda表達式的Where查詢)
23: /// <summary>
24: /// 獲取所有Entity
25: /// </summary>
26: /// <param name="exp">Lambda條件的where</param>
27: /// <returns></returns>
28: public virtual IEnumerable<T> GetEntities(Func<T, bool> exp)
29: {
30:
31: using (BaseManageEntities Entities = new BaseManageEntities())
32: {
33: return Entities.CreateObjectSet<T>().Where(exp).ToList();
34: }
35:
36: }
37: /// <summary>
38: /// 計算總個數(分頁)
39: /// </summary>
40: /// <param name="exp">Lambda條件的where</param>
41: /// <returns></returns>
42: public virtual int GetEntitiesCount(Func<T, bool> exp)
43: {
44: using (BaseManageEntities Entities = new BaseManageEntities())
45: {
46: return Entities.CreateObjectSet<T>().Where(exp).Count();
47: }
48: }
49: /// <summary>
50: /// 分頁查詢(Linq分頁方式)
51: /// </summary>
52: /// <param name="pageNumber">當前頁</param>
53: /// <param name="pageSize">頁碼</param>
54: /// <param name="orderName">lambda排序名稱</param>
55: /// <param name="sortOrder">排序(升序or降序)</param>
56: /// <param name="exp">lambda查詢條件where</param>
57: /// <returns></returns>
58: public virtual IEnumerable<T> GetEntitiesForPaging(int pageNumber, int pageSize, Func<T, string> orderName, string sortOrder, Func<T, bool> exp)
59: {
60: using (BaseManageEntities Entities = new BaseManageEntities())
61: {
62: if (sortOrder=="asc") //升序排列
63: {
64: return Entities.CreateObjectSet<T>().Where(exp).OrderBy(orderName).Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
65: }
66: else
67: return Entities.CreateObjectSet<T>().Where(exp).OrderByDescending(orderName).Skip((pageNumber - 1) * pageSize).Take(pageSize).ToList();
68: }
69:
70: }
71: /// <summary>
72: /// 根據條件查找
73: /// </summary>
74: /// <param name="exp">lambda查詢條件where</param>
75: /// <returns></returns>
76: public virtual T GetEntity(Func<T, bool> exp)
77: {
78: using (BaseManageEntities Entities = new BaseManageEntities())
79: {
80: return Entities.CreateObjectSet<T>().Where(exp).SingleOrDefault();
81: }
82: }
83: #endregion
84: #region 查詢Entity To Sql語句外接接口的查詢實現
85: /// <summary>
86: /// 獲取所有Entity(立即執行請使用ToList()
87: /// </summary>
88: /// <param name="CommandText">Sql語句</param>
89: /// <param name="objParams">可變參數</param>
90: /// <returns></returns>
91: public virtual IEnumerable<T> GetEntities(string CommandText)
92: {
93:
94: using (BaseManageEntities Entities = new BaseManageEntities())
95: {
96: return Entities.ExecuteStoreQuery<T>("select * from "+typeof(T).Name+" where "+CommandText).ToList();
97: }
98:
99: }
100: /// <summary>
101: /// 計算總個數(分頁)
102: /// </summary>
103: /// <param name="CommandText">Sql語句</param>
104: /// <returns></returns>
105: public virtual int GetEntitiesCount(string CommandText)
106: {
107: using (BaseManageEntities Entities = new BaseManageEntities())
108: {
109: return Entities.ExecuteStoreQuery<T>("select * from " +typeof(T).Name+ " where "+CommandText).Count();
110: }
111: }
112:
113: /// <summary>
114: /// 分頁查詢(Linq分頁方式)
115: /// </summary>
116: /// <param name="tableName">表名</param>
117: /// <param name="pageNumber">當前頁</param>
118: /// <param name="pageSize">頁碼</param>
119: /// <param name="orderName">lambda排序名稱</param>
120: /// <param name="sortOrder">排序(升序or降序)</param>
121: /// <param name="CommandText">Sql語句</param>
122: /// <param name="Count">總個數</param>
123: /// <returns></returns>
124: public virtual IEnumerable<T> GetEntitiesForPaging(string tableName, int pageNumber, int pageSize, string orderName, string sortOrder, string CommandText,out int Count)
125: {
126: PaginationHelper pager = new PaginationHelper(tableName, orderName, pageSize, pageNumber, sortOrder, CommandText);
127: pager.GetSelectTopByMaxOrMinPagination();
128: using (BaseManageEntities Entities = new BaseManageEntities())
129: {
130: Count =GetEntitiesCount(CommandText);
131: return Entities.ExecuteStoreQuery<T>(pager.GetSelectTopByMaxOrMinPagination()).ToList();
132:
133: }
134:
135: }
136: /// <summary>
137: /// 根據條件查找
138: /// </summary>
139: /// <param name="CommandText">Sql語句</param>
140: /// <param name="objParams">可變參數</param>
141: /// <returns></returns>
142: public virtual T GetEntity(string CommandText)
143: {
144: using (BaseManageEntities Entities = new BaseManageEntities())
145: {
146: return Entities.ExecuteStoreQuery<T>("select * from "+typeof(T).Name+" where "+CommandText).SingleOrDefault();
147: }
148: }
149: #endregion
150: #region 增刪改實現
151: /// <summary>
152: /// 插入Entity
153: /// </summary>
154: /// <param name="model"></param>
155: /// <returns></returns>
156: public virtual bool Insert(T entity)
157: {
158: using (BaseManageEntities Entities = new BaseManageEntities())
159: {
160: var obj = Entities.CreateObjectSet<T>();
161: obj.AddObject(entity);
162: return Entities.SaveChanges() > 0;
163: }
164: }
165: /// <summary>
166: /// 更新Entity(注意這里使用的傻瓜式更新,可能性能略低)
167: /// </summary>
168: /// <param name="model"></param>
169: /// <returns></returns>
170: public virtual bool Update(T entity)
171: {
172: using (BaseManageEntities Entities = new BaseManageEntities())
173: {
174: var obj = Entities.CreateObjectSet<T>();
175: obj.Attach(entity);
176: Entities.ObjectStateManager.ChangeObjectState(entity, EntityState.Modified);
177: return Entities.SaveChanges() > 0;
178: }
179:
180: }
181: /// <summary>
182: /// 刪除Entity
183: /// </summary>
184: /// <param name="entity"></param>
185: /// <returns></returns>
186: public virtual bool Delete(T entity)
187: {
188: using (BaseManageEntities Entities = new BaseManageEntities())
189: {
190: var obj = Entities.CreateObjectSet<T>();
191:
192: if (entity != null)
193: {
194: obj.Attach(entity);
195: Entities.ObjectStateManager.ChangeObjectState(entity, EntityState.Deleted);
196:
197: obj.DeleteObject(entity);
198: return Entities.SaveChanges() > 0;
199:
200: }
201: return false;
202: }
203: }
204:
205: #endregion
206:
207:
208:
209: }
210: }
接下來我們的工作量就小了,我們可以看到成果了,我們接下來的實現,只需要繼承上面這個BaseEFDao和IxxxDao的部分就可以很輕松的實現,子類的增刪改查以及子類的擴展了.
1: using System;
2: using System.Collections.Generic;
3: using System.Linq;
4: using System.Text;
5: using TZHSWEET.Entity;
6: using TZHSWEET.IDao;
7: namespace TZHSWEET.EFDao
8: {
9: public class FavoriteEFDao : BaseEFDao<tbFavorite>, IFavoriteDao<tbFavorite>
10: {
11:
12: }
13: }
大家可以看到繼承了2個,一個是BaseEFDao父類,一個是IFavoriteDao接口,
BaseEFDao是實現EF的增刪改查,IFavoriteDao實現了子類的擴展,比如子類需要加入
GetxxxByID之類的,當然我們在業務邏輯層一般只能通過IFavoriteDao調用此服務,而
這個繼承體系保證了我們在實現增刪改查的同時外放一個接口保證擴展性.
繼承體系圖:
這樣我們的數據訪問層,很輕松的實現了,基本上代碼量非常少,增刪改查部分幾乎"0"代碼,都是泛型的父類實現了.
今天就講解到這里,接下來插播點當前有趣的實例部分:
界面友好,友好的名稱遠遠比注釋更好,<<重構>>這本書,讓我學到太多太多,特別是細節的設計,更清晰.
可以參考,我的代碼風格,可以看到我的進步非常大,我現在看到2-3個月以前的代碼,簡直都是<<重構>>這本書的反面教材.
當前的權限管理系統,設計了一個初步的模型,這里貼張小圖:
參考了LigerUI,謝略的權限管理系統,吉日的權限管理數據庫設計和N多日志,以及傳說中的弦哥的Asp.Net大型項目實踐系列教程,以及眾多的資料,慢慢的我也會把這個寫成一個系列.最后,寫完這個教程,我會把代碼傳到公共網盤上面,不過由於現在代碼持續改進,所以,就大家先看看思路.