前言
在軟件項目開發過程中,我們總能聽見“高內聚,低耦合”,即使這種思想在我們學習編程的過程中就已經耳濡目染。可一旦當我們上項目,趕進度的時候我們就會“偷懶”,能省時間就省。管他什么設計模式,什么軟件架構先實現上線再說。當然,如果這是一個一次性項目,或者是一個比較簡單的項目還好說,但如果這個項目牽扯到后期的維護再開發,那么之前的“偷懶”就會成為“技術債”。最近剛研究完EF框架,寫個demo練練手,正好貼出來做個拋磚引玉的作用。希望大家一起討論,共同進步!
基礎框架搭建
基礎架構也就是常用的三層架構,UI層:MVC,數據庫訪問驅動層:Entity Framework,由於是演示在這里我們的Model層就用兩個表做演示了(T_UserInfo,T_OrderInfo)
數據庫訪問層設計
在基礎框架搭建完畢后,我們再設計一下數據庫訪問驅動層DAL。首先我們抽象一個BaseDAL,寫一個公共的CRUD。
1 namespace Smp.Demo.EFDAL 2 { 3 public class BaseDal<T> where T : class,new() 4 { 5 SmpDBEntities m_dbContext = new SmpDBEntities(); 6 7 // 條件查詢 8 public virtual IQueryable<T> GetEntities(Expression<Func<T, bool>> whereLambda) 9 { 10 return m_dbContext.Set<T>().Where(whereLambda); 11 } 12 13 // 分頁查詢 14 public virtual IQueryable<T> GetPageEntities<S>(int pageSize, int pageIndex, out int totalCount, 15 Func<T, bool> whereLambda, Func<T, S> orderLambda) 16 { 17 totalCount = m_dbContext.Set<T>().Count(); 18 return m_dbContext.Set<T>().Where(whereLambda). 19 OrderBy(orderLambda).Skip((pageIndex - 1) * pageSize). 20 Take(pageSize * pageIndex).AsQueryable(); 21 } 22 23 // 添加 24 public virtual T Add(T dbInfo) 25 { 26 m_dbContext.Set<T>().Add(dbInfo); 27 m_dbContext.SaveChanges(); 28 return dbInfo; 29 } 30 31 // 刪除 32 public virtual bool Delete(T dbInfo) 33 { 34 m_dbContext.Entry<T>(dbInfo).State = System.Data.EntityState.Deleted; 35 return m_dbContext.SaveChanges() > 0; 36 } 37 38 // 修改 39 public virtual bool Update(T dbInfo) 40 { 41 m_dbContext.Entry(dbInfo).State = System.Data.EntityState.Modified; 42 return m_dbContext.SaveChanges() > 0; 43 } 44 } 45 }
然后我們封裝T_UserInfo和T_OrderInfo的數據庫訪問層,直接繼承BaseDAL即可,繼承后子類中有特殊需求單獨實現即可。
1 namespace Smp.Demo.EFDAL 2 { 3 public class T_UserInfoDAL : BaseDal<T_UserInfo> 4 { 5 6 } 7 } 8 9 namespace Smp.Demo.EFDAL 10 { 11 public class T_OrderInfoDAL : BaseDal<T_OrderInfo> 12 { 13 14 } 15 }
業務邏輯層設計
業務邏輯層在三層架構中權重是比較高的一層,該層在項目中起到一個承上啟下的作用,既要操作數據訪問層的CRUD,也要處理UI層的業務邏輯。那么接下來我們就以T_UserInfo來寫一個業務邏輯層的演示Demo
1 namespace Smp.Demo.BLL 2 { 3 public class T_UserInfoBLL 4 { 5 T_UserInfoDAL m_userInfoDAL = new T_UserInfoDAL(); 6 7 // 獲取全部用戶信息 8 public List<T_UserInfo> GetAllUserInfo() 9 { 10 return m_userInfoDAL.GetEntities(userInfo => true).ToList(); 11 } 12 13 // 添加用戶並返回新增用戶ID 14 public int AddUser(T_UserInfo userInfo) 15 { 16 var l_newUserInfo = m_userInfoDAL.Add(userInfo); 17 return l_newUserInfo.ID; 18 } 19 20 // 刪除指定用戶 21 public bool Delete(T_UserInfo userInfo) 22 { 23 return m_userInfoDAL.Delete(userInfo); 24 } 25 } 26 }
當我們DAL層和BLL層的框架寫完后,我們在當前項目中創建一個測試單元,測試以上框架是否能夠正常運行
經測試,就目前而言我們搭建的架構是可以正常運行的,那么我們來看一下當前DAL層和BLL層之間的UML圖
思考
通過以上的UML圖和代碼我們可以看到,每個模塊都相對獨立,基本上完成了“高內聚”的思想。但唯一不足的就是BLL層和DAL之間的連接總是不那么盡如人意。例如存在這樣的需求,當用戶量大了以后DAL層想從EF框架換為NH框架,或者是想將數據庫從SqlServer換為Oracle。那么我們以上的設計是不符合“低耦合”思想的,因為BLL層和DAL層的依賴是那么的強,一旦數據庫訪問驅動層更換那么BLL層和DAL層的改動是非常多的。怎么辦呢?我想大家在開發過程中可能聽過這么一句話“面向接口編程”。那么我們也來對BLL層和DAL層之間面向接口編程吧!
數據訪問驅動層接口設計
首先,在當前項目中新增一個“IDAL”接口項目。然后我們對DAL層的CRUD來進行一個抽象。
1 namespace Smp.Demo.IDAL 2 { 3 public interface IBaseDAL<T> where T : class ,new() 4 { 5 // 條件查詢 6 IQueryable<T> GetEntities(Expression<Func<T, bool>> whereLambda); 7 8 // 條件分頁查詢 9 IQueryable<T> GetPageEntities<S>(int pageSize, int pageIndex, out int totalCount, 10 Func<T, bool> whereLambda, Func<T, S> orderLambda); 11 12 // 添加 13 T Add(T dbInfo); 14 15 // 刪除 16 bool Delete(T dbInfo); 17 18 // 更新 19 bool Update(T dbInfo); 20 } 21 }
其次我們再繼承“IDAL”寫IUserInfo和IOrderInfo數據庫訪問驅動層接口
1 namespace Smp.Demo.IDAL 2 { 3 public interface IUserInfo : IBaseDAL<T_UserInfo> 4 { 5 } 6 } 7 8 namespace Smp.Demo.IDAL 9 { 10 public interface IOrderInfo : IBaseDAL<T_OrderInfo> 11 { 12 } 13 }
然后我們再將EFDAL層中的T_OrderInfoDAL和T_UserInfoDAL再分別繼承IUserInfo和IOrderInfo
1 namespace Smp.Demo.EFDAL 2 { 3 public class T_OrderInfoDAL : BaseDal<T_OrderInfo>,IOrderInfo 4 { 5 6 } 7 } 8 9 namespace Smp.Demo.EFDAL 10 { 11 public class T_UserInfoDAL : BaseDal<T_UserInfo>,IUserInfo 12 { 13 14 } 15 }
重新設計后UML圖是這樣的,也就是在BLL層和DAL層之間加了個中間層,方便后期動態配置數據訪問驅動層。降低耦合程度
當我們將接口設計好后,我們再在當前項目下創建一個DALFactory抽象工廠項目,用於后期基於Web.config文件動態獲取DAL層訪問
抽象工廠模式的創建
第一步、打開UI層在Web.confg文件中找到appSettings節點中添加后期動態創建的DAL層程序集節點名稱方便后期反射指定程序集
第二步、創建一個新建項目DALFactory抽象工廠,用戶按照Web.config動態創建指定DAL數據庫訪問驅動
1 namespace Smp.Demo.DALFactory 2 { 3 public static class StaticDALFactory 4 { 5 public static IUserInfo GetUserInfoDAL() 6 { 7 string l_strAssemblyName = System.Configuration.ConfigurationManager.AppSettings["AssemblyName"]; 8 return Assembly.Load(l_strAssemblyName).CreateInstance(l_strAssemblyName + ".T_UserinfoDAL") as IUserInfo; 9 } 10 11 public static IOrderInfo GetOrderInfoDAL() 12 { 13 string l_strAssemblyName = System.Configuration.ConfigurationManager.AppSettings["AssemblyName"]; 14 return Assembly.Load(l_strAssemblyName).CreateInstance(l_strAssemblyName + ".T_Orderinfo") as IOrderInfo; 15 } 16 } 17 }
第三步、修改BLL層中訪問DAL層的訪問方式
1 namespace Smp.Demo.BLL 2 { 3 public class T_UserInfoBLL 4 { 5 IUserInfo m_userInfoDAL = StaticDALFactory.GetUserInfoDAL(); 6 7 // 獲取全部用戶信息 8 public List<T_UserInfo> GetAllUserInfo() 9 { 10 return m_userInfoDAL.GetEntities(userInfo => true).ToList(); 11 } 12 13 // 添加用戶並返回新增用戶ID 14 public int AddUser(T_UserInfo userInfo) 15 { 16 var l_newUserInfo = m_userInfoDAL.Add(userInfo); 17 return l_newUserInfo.ID; 18 } 19 20 // 刪除指定用戶 21 public bool Delete(T_UserInfo userInfo) 22 { 23 return m_userInfoDAL.Delete(userInfo); 24 } 25 } 26 }
第四步、將EFDAL中app.config的連接字符串配置到UI層web.config中
第五步、在UI層中創建一個demo控制器,並同時創建一個Index視圖
1 namespace Smp.Demo.UI.Controllers 2 { 3 public class DemoController : Controller 4 { 5 // 6 // GET: /Demo/ 7 public ActionResult Index() 8 { 9 var Users = new T_UserInfoBLL().GetAllUserInfo(); 10 ViewData["users"] = Users; 11 return View(); 12 } 13 } 14 } 15 16 @{ 17 Layout = null; 18 } 19 20 <!DOCTYPE html> 21 22 <html> 23 <head> 24 <title>Index</title> 25 </head> 26 <body> 27 <div> 28 @foreach (var item in ViewData["users"] as List<Smp.Demo.Model.T_UserInfo>) 29 { 30 <p> 31 @item.UserName 32 </p> 33 } 34 </div> 35 </body> 36 </html>
最后效果
這樣的話后期我們就可以很方便的擴展數據訪問層啦!