模塊內高內聚?模塊間低耦合?MVC+EF演示給你看!


前言  

  在軟件項目開發過程中,我們總能聽見“高內聚,低耦合”,即使這種思想在我們學習編程的過程中就已經耳濡目染。可一旦當我們上項目,趕進度的時候我們就會“偷懶”,能省時間就省。管他什么設計模式,什么軟件架構先實現上線再說。當然,如果這是一個一次性項目,或者是一個比較簡單的項目還好說,但如果這個項目牽扯到后期的維護再開發,那么之前的“偷懶”就會成為“技術債”。最近剛研究完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>

最后效果

 這樣的話后期我們就可以很方便的擴展數據訪問層啦!

源碼下載地址:https://github.com/p9966/Smp.Demo


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM