第九节:基于MVC5+AutoFac+EF+Log4Net的基础结构搭建
一. 前言
從本節(jié)開(kāi)始,將陸續(xù)的介紹幾種框架搭建組合形式,分析每種搭建形式的優(yōu)勢(shì)和弊端,剖析搭建過(guò)程中涉及到的一些思想和技巧。
(一). 技術(shù)選型
1. DotNet框架:4.6
2. 數(shù)據(jù)庫(kù)訪問(wèn):EF 6.2 (CodeFrist模式)
3. IOC框架:AutoFac 4.8.1 和 AutoFac.MVC5 4.0.2
4. 日志框架:log4net 2.0.8
5. 開(kāi)發(fā)工具:VS2017
(二). 框架目標(biāo)
1.?一個(gè)項(xiàng)目同時(shí)連接多個(gè)相同種類的數(shù)據(jù)庫(kù),在一個(gè)方法中可以同時(shí)對(duì)多個(gè)數(shù)據(jù)進(jìn)行操作。
2.?支持多種數(shù)據(jù)庫(kù):SqlServer、MySQL、Oracle,靈活的切換數(shù)據(jù)庫(kù)。
3.?抽象成支持多種數(shù)據(jù)庫(kù)連接方式:EF、ADO.Net、Dapper。
?
二. 搭建思路
?1. 層次劃分
將框架分為:Ypf.Data、Ypf.IService、Ypf.Service、Ypf.DTO、Ypf.Utils、Ypf.AdminWeb 六個(gè)基本層(后續(xù)還會(huì)補(bǔ)充 Ypf.Api層),每層的作用分別為:
①.?Ypf.Data:存放連接數(shù)據(jù)庫(kù)的相關(guān)類,包括EF上下文類、映射的實(shí)體類、實(shí)體類的FluentApi模式的配置類。
②.?Ypf.IService:業(yè)務(wù)接口層,用來(lái)約束接口規(guī)范。
③.?Ypf.Service:業(yè)務(wù)層,用來(lái)編寫整套項(xiàng)目的業(yè)務(wù)方法,但需要符合Ypf.IService層的接口約束。
④.?Ypf.DTO: 存放項(xiàng)目中用到的自定義的實(shí)體類。
⑤.?Ypf.Utils: 工具類
⑥.?Ypf.AdminWeb: 表現(xiàn)層,系統(tǒng)的展示、接受用戶的交互,傳遞數(shù)據(jù),業(yè)務(wù)對(duì)接。
PS:后續(xù)會(huì)補(bǔ)充Ypf.Api層,用于接受移動(dòng)端、或其他客戶端接口數(shù)據(jù),進(jìn)行相應(yīng)的業(yè)務(wù)對(duì)接處理。
2. Ypf.Data層的剖析
把EF封裝到Y(jié)pf.Data層,通過(guò)Nuget引入EF的程序集,利用FluentAPI的模式先進(jìn)行配置(實(shí)際項(xiàng)目多種模式配合使用),該層的結(jié)構(gòu)如下:
PS:EF的關(guān)閉默認(rèn)策略、EF的DataAnnotations、EF的FluentAPI模式, 在關(guān)閉數(shù)據(jù)庫(kù)策略的情況下,無(wú)論哪種模式都需要顯式的 ToTable來(lái)映射表名,否則會(huì)提示該類找不到。
EF配置詳情參考:
? ? ? ? ?第十五節(jié): EF的CodeFirst模式通過(guò)DataAnnotations修改默認(rèn)協(xié)定
? ? ? ? ?第十六節(jié): EF的CodeFirst模式通過(guò)Fluent API修改默認(rèn)協(xié)定
3.?Service層和IService層簡(jiǎn)單的封裝一下
【PS:這個(gè)地方是個(gè)關(guān)鍵點(diǎn),需要考慮多種不同的寫法,然后進(jìn)行封裝】
①.【Ypf.Service】層只有一個(gè)BaseService泛型類封裝,【Ypf.IService】層并沒(méi)有設(shè)置IBaseService接口,設(shè)置了一個(gè)IServiceSupport標(biāo)識(shí)接口(沒(méi)有任何內(nèi)容),需要“AutoFac注入”的所有子類IxxxService都要實(shí)現(xiàn)IServiceSupport接口。
②.【Ypf.Service】層中有很多自定義的 xxxService,每個(gè)xxxService都要實(shí)現(xiàn)【Ypf.IService】層的IxxxService層接口,這里的xxxService層劃分并不依賴表名劃分,自定義根據(jù)業(yè)務(wù)合理起名即可。
③. xxxService類中,利用using() 包裹EF的上下文“db”便于釋放,然后把EF上下文傳入到泛型的BaseService<T>類的構(gòu)造函數(shù)中,可以調(diào)用其封裝的方法。
④.利用AutoFac實(shí)現(xiàn)在控制器中屬性的注入,相應(yīng)的配置均寫在Global文件中。
⑤.控制器中的Action僅僅負(fù)責(zé)傳值和簡(jiǎn)單的一些判斷,核心業(yè)務(wù)全部都寫在Service層中。
4. 利用AutoFac實(shí)現(xiàn)Ypf.AdminWeb層與Ypf.Service層解耦
利用AutoFac進(jìn)行整合,使Ypf.AdminWeb層只需要引入YpfIService層即可,但需要改一下Ypf. Service輸出路徑使其程序集輸出到Y(jié)pf.AdminWeb層中。
?解析:利用AutoFac把Ypf.Service中的所有類注冊(cè)給它的全部實(shí)現(xiàn)接口(一個(gè)類可能實(shí)現(xiàn)了多個(gè)接口),并且把實(shí)現(xiàn)類中的屬性也進(jìn)行注冊(cè)(實(shí)現(xiàn)類中也可能包含屬性的注入)。
AutoFac的配置詳情參考:?
? ? ? ? ?第二節(jié):框架前期準(zhǔn)備篇之AutoFac常見(jiàn)用法總結(jié)
5. 將Log4net整合到Y(jié)pf.Utils層中
解析:主要配置了兩種模式,輸出到“txt文本文檔”和“SQLServer數(shù)據(jù)庫(kù)中”。其中“文本文檔”又分了兩種模式,全部輸入到一個(gè)文檔中 和 不同類型的日志輸入到不同文檔下,在調(diào)用的時(shí)候通過(guò)傳入?yún)?shù)來(lái)區(qū)分存放在哪個(gè)文件夾下。
Log4net的配置詳情參考:
第一節(jié):框架前期準(zhǔn)備篇之Log4Net日志詳解
6.?完善【Ypf.Service】層中BaseService的封裝
? 封裝EF常用的增刪改查的方法,這里暫時(shí)先不擴(kuò)展EF插件的方法,分享一下代碼。
?BaseService
?
三. 剖析核心
1. 如何實(shí)現(xiàn)同時(shí)操作多個(gè)相同類型的不同結(jié)構(gòu)的數(shù)據(jù)庫(kù)。
首先【Ypf.Data】層中新建一個(gè)存放的實(shí)體的文件夾,如“EntityTest”,用來(lái)引入另外一個(gè)數(shù)據(jù)庫(kù)的存放實(shí)體和DbContext上下文,然后在【Ypf.Service】層中,雙Using,往BaseService類中傳入不同db上下文即可實(shí)現(xiàn)訪問(wèn)不同的數(shù)據(jù)庫(kù),如果要對(duì)多個(gè)數(shù)據(jù)庫(kù)開(kāi)啟事務(wù),手動(dòng)開(kāi)啟msdtc服務(wù),然后使用Transactions包裹,進(jìn)行事務(wù)一體操作。
詳細(xì)的使用步驟見(jiàn):實(shí)戰(zhàn)測(cè)試。
2.?體會(huì)【Ypf.IService】層 和 引入IOC框架的作用
【PS:依賴倒置原則的核心就是:面向接口編程】
(1). 接口層的作用:
a. 便于開(kāi)發(fā)人員分工開(kāi)發(fā),寫業(yè)務(wù)的單獨(dú)去寫業(yè)務(wù),對(duì)接的單獨(dú)去對(duì)接,而且事先把接口協(xié)議定好,那么對(duì)接的人員就不需要等業(yè)務(wù)人員全部寫完代碼,就可以對(duì)接了,無(wú)非最后再測(cè)試而已。
b. 降低修改代碼造成的成本代價(jià),使以接口為基礎(chǔ)搭建起來(lái)的框架更加穩(wěn)健。
舉例1:?三層架構(gòu) 數(shù)據(jù)庫(kù)訪問(wèn)層、業(yè)務(wù)邏輯層、UI調(diào)用層。?(非此套框架的模式,后面考慮這么改進(jìn))
①. 數(shù)據(jù)庫(kù)訪問(wèn)層中有一個(gè) MySqlHelp類,提供鏈接MySQL數(shù)據(jù)增刪改查的方法。
②. 業(yè)務(wù)邏輯層有一個(gè)登錄業(yè)務(wù) CheckLogin(MySqlHelp mysql,string userName,string pwd)。
③. UI調(diào)用層要調(diào)用CheckLogin方法,這時(shí)候?qū)嵗粋€(gè)MySqlHelp對(duì)象,傳到CheckLogin方法中即可。
有一個(gè)天,要求支持oracle數(shù)據(jù)庫(kù),所以數(shù)據(jù)庫(kù)訪問(wèn)層中增加了一個(gè)oracleHelper類,UI調(diào)用層按照常規(guī)實(shí)例化了一個(gè)oracleHelper對(duì)象,傳到CheckLogin方法中,發(fā)現(xiàn)我的天!!!!CheckLogin竟然不支持oracleHelper對(duì)象,同時(shí)發(fā)現(xiàn)類似的所有業(yè)務(wù)層的方法都不支持oracleHelper類,這個(gè)時(shí)候悲劇就發(fā)生了,如果全部改業(yè)務(wù)層的方法,基本上完蛋。
所以根本的解決方案:依賴倒置原則,即面向接口編程。
①. 數(shù)據(jù)庫(kù)訪問(wèn)層聲明一個(gè)接口IHelper,里面有增刪改查方法,MySqlHelp和oracleHelper都實(shí)現(xiàn)IHelper接口。
②. 業(yè)務(wù)邏輯層有一個(gè)登錄業(yè)務(wù)改為依賴接口IHelper, CheckLogin(IHelper iHelper,string userName,string pwd)。
③. UI調(diào)用層要調(diào)用CheckLogin方法,想連哪個(gè)數(shù)據(jù),就實(shí)例化哪個(gè) eg IHelper iHelper=new MySqlHelp(); 或者 IHelper iHelper=new oracleHelper(),此處考慮和IOC框架結(jié)合,連代碼都不用改,直接改配置文件就行了,就可以切換實(shí)例,然后調(diào)用CheckLogin即可。
舉例2:?類A,類B,類C。
類A中的方法需要傳入類B的實(shí)例,通常在類A中實(shí)例化一下類B,但如果想讓類A依賴類C,你會(huì)發(fā)現(xiàn)改動(dòng)非常大,類A中的方法原先是類B的參數(shù)全部需要改。
所以解決方案:類B和類C都實(shí)現(xiàn)接口I,類A中方法的參數(shù)由原先的類B改為接口I,這樣類A想依賴誰(shuí),只需要 I i=new B() 或者 I i=new C(),所有的方法都不用改,也可以再升級(jí)一下,這里不直接實(shí)例化,利用IOC框架或者手寫反射,只需要改一下配置文件,就能控制 到底是 new B 還是 new C 。
?(2). 引入IOC框架的作用:
解決的問(wèn)題1:現(xiàn)有的框架模式(Service層using引入EF上下文,傳入到BaseService類中),如何實(shí)現(xiàn)快速切換數(shù)據(jù)庫(kù)?
a.首先在【Ypf.Data】層引入MySQL數(shù)據(jù)庫(kù)所需要的程序集,配置文件也改成連接MySQL的。(此處需要詳細(xì)測(cè)試)
b. 新建一個(gè)【Ypf.Service2】層,同樣實(shí)現(xiàn)對(duì)應(yīng)業(yè)務(wù),只不過(guò)是連接不同類型的數(shù)據(jù)庫(kù)(比如它連接的是MySql數(shù)據(jù)庫(kù)),生成路徑也輸出到【Ypf.AdminWeb】層中,最后只需要改一下AutoFac讀取的配置文件“DllName”改為“Ypf.Services2”即可,就可以實(shí)現(xiàn)切換數(shù)據(jù)。
總結(jié):該模式雖然能實(shí)現(xiàn)“相同業(yè)務(wù)、相同表”的不同類型的數(shù)據(jù)庫(kù)切換(比如SQLServer→MySQL),但是需要重新寫一個(gè)整層【Ypf.Service2】,雖然基本上是復(fù)制,但是有一定工作量的。但是另外通過(guò)手寫IOC也可以實(shí)現(xiàn)(反射+簡(jiǎn)單工廠+配置文件),看不到IOC框架的優(yōu)勢(shì)所在。
IOC強(qiáng)大之處在于框架本身為我們封裝好了很多便于開(kāi)發(fā)的方法,拿AutoFac來(lái)說(shuō)吧,能靈活的控制創(chuàng)建對(duì)象的(每次請(qǐng)求都創(chuàng)建、單例、一個(gè)Http請(qǐng)求內(nèi)單例)
四. 實(shí)戰(zhàn)測(cè)試
這里準(zhǔn)備兩個(gè)數(shù)據(jù)庫(kù),分別是:YpfFrame_DB 和?YpfFrameTest_DB
①:YpfFrame_DB中,用到了表:T_SysUser 和?T_SysLoginLog,表結(jié)構(gòu)如下
②.?YpfFrameTest_DB 表中用到了T_SchoolInfor,表結(jié)構(gòu)如下
開(kāi)始測(cè)試
1. 測(cè)試增刪改查,包括基本的事務(wù)一體。
在【Ypf.IService】層中新建ITestService接口,在【Ypf.Service】層中新建TestService類,實(shí)現(xiàn)ITestService接口, 定義TestBasicCRUD方法,進(jìn)行測(cè)試,代碼如下。
1 /// <summary>2 /// 1.測(cè)試基本的增刪改查,事務(wù)一體3 /// </summary>4 /// <returns></returns>5 public int TestBasicCRUD()6 {7 using (DbContext db = new MyDBContext1())8 {9 BaseService<T_SysUser> T_SysUserService = new BaseService<T_SysUser>(db); 10 BaseService<T_SysLoginLog> T_SysLoginLogService = new BaseService<T_SysLoginLog>(db); 11 //1.增加操作 12 T_SysUser t_SysUser = new T_SysUser() 13 { 14 id = Guid.NewGuid().ToString("N"), 15 userAccount = "123456", 16 userPwd = "XXX", 17 userRealName = "XXX", 18 appLoginNum = 1, 19 addTime = DateTime.Now 20 }; 21 T_SysUserService.AddNo(t_SysUser); 22 23 //2.修改操作 24 T_SysLoginLog t_SysLoginLog = T_SysLoginLogService.Entities.Where(u => u.id == "1").FirstOrDefault(); 25 if (t_SysLoginLog != null) 26 { 27 t_SysLoginLog.userId = "xxx"; 28 t_SysLoginLog.userName = "xxx"; 29 T_SysLoginLogService.ModifyNo(t_SysLoginLog); 30 } 31 //3.提交操作 32 return db.SaveChanges(); 33 } 34 }2. 測(cè)試一個(gè)方法中查詢多個(gè)數(shù)據(jù)庫(kù)。
在ITestService接口中定義ConnectManyDB方法,并在TestService中實(shí)現(xiàn)該方法,代碼如下:
1 /// <summary>2 /// 2. 同時(shí)連接多個(gè)數(shù)據(jù)庫(kù)進(jìn)行3 /// </summary>4 /// <param name="userList"></param>5 /// <param name="schoolList"></param>6 public void ConnectManyDB(out List<T_SysUser> userList, out List<T_SchoolInfor> schoolList)7 {8 using (DbContext db = new MyDBContext1())9 using (DbContext db2 = new MyDBContext2()) 10 { 11 BaseService<T_SysUser> T_SysUserService = new BaseService<T_SysUser>(db); 12 BaseService<T_SchoolInfor> T_SchoolInforService = new BaseService<T_SchoolInfor>(db2); 13 14 //執(zhí)行數(shù)據(jù)庫(kù)查詢操作 15 userList = T_SysUserService.GetListBy(u => true); 16 schoolList = T_SchoolInforService.GetListBy(u => true); 17 } 18 }分析:想連接幾個(gè)數(shù)據(jù)庫(kù),就需要先在【Ypf.Data】層中新建對(duì)應(yīng)數(shù)據(jù)庫(kù)的實(shí)體、實(shí)體配置文件、EF上下文,然后在【Ypf.Service】層對(duì)應(yīng)的方法中實(shí)例化對(duì)應(yīng)的 EF上下文,然后傳入到BaseService類中即可。
3. 測(cè)試一個(gè)方法中事務(wù)一體處理多個(gè)數(shù)據(jù)庫(kù)的crud操作。
?在ITestService接口中定義ManyDBTransaction方法,并在TestService中實(shí)現(xiàn)該方法,代碼如下:
1 /// <summary>2 /// 3. 同時(shí)對(duì)多個(gè)數(shù)據(jù)庫(kù)進(jìn)行事務(wù)一體的CRUD操作3 /// 注:需要手動(dòng)開(kāi)啟msdtc服務(wù)(net start msdtc)4 /// </summary>5 public void ManyDBTransaction()6 {7 using (TransactionScope trans = new TransactionScope())8 {9 try 10 { 11 DbContext db = new MyDBContext1(); 12 DbContext db2 = new MyDBContext2(); 13 14 BaseService<T_SysUser> T_SysUserService = new BaseService<T_SysUser>(db); 15 BaseService<T_SchoolInfor> T_SchoolInforService = new BaseService<T_SchoolInfor>(db2); 16 17 //執(zhí)行業(yè)務(wù)操作 18 T_SysUserService.DelBy(u => u.id == "1"); 19 T_SchoolInforService.DelBy(u => u.id == "1"); 20 21 //最終提交事務(wù) 22 trans.Complete(); 23 } 24 catch (Exception ex) 25 { 26 var msg = ex.Message; 27 //事務(wù)回滾 28 Transaction.Current.Rollback(); 29 throw; 30 } 31 } 32 }分析:同時(shí)連接多個(gè)數(shù)據(jù)庫(kù),并對(duì)多個(gè)數(shù)據(jù)庫(kù)進(jìn)行事務(wù)性的crud操作,這個(gè)時(shí)候必須用 【TransactionScope事務(wù)】,前提要手動(dòng) 【net start msdtc?】開(kāi)啟對(duì)應(yīng)服務(wù),這樣整個(gè)事務(wù)通過(guò)“Complete”方法進(jìn)行提交,通過(guò)Transaction.Current.Rollback()方法進(jìn)行事務(wù)回滾,各自db的SaveChange不起作用,但還是需要SaveChange的。
4. 測(cè)試xxxSevice子類中也可以通過(guò)AutoFac進(jìn)行IxxxService的模式進(jìn)行屬性的注入。
?在【Ypf.IService】層中新建ITestService2接口,在【Ypf.Service】層中新建TestService2類,實(shí)現(xiàn)ITestService接口, 定義GetUserInfor方法,進(jìn)行測(cè)試,代碼如下。
?View Code
在TestService中定義ITestService2屬性,如下:
在TestService中定義如下方法,內(nèi)部用TestService2進(jìn)行調(diào)用,可以調(diào)用成功,從而證明xxxSevice子類中也可以通過(guò)AutoFac進(jìn)行IxxxService的模式進(jìn)行屬性的注入。
5. 測(cè)試Log4net的分文件夾和不分文件的使用。
?先分享配置文件:
?View Code
分享對(duì)應(yīng)的封裝類:
?View Code
代碼測(cè)試:
?
?
?
?
?
!
- 作???????者 :?Yaopengfei(姚鵬飛)
- 博客地址 :?http://www.cnblogs.com/yaopengfei/
- 聲?????明1 : 本人才疏學(xué)淺,用郭德綱的話說(shuō)“我是一個(gè)小學(xué)生”,如有錯(cuò)誤,歡迎討論,請(qǐng)勿謾罵^_^。
- 聲?????明2 : 原創(chuàng)博客請(qǐng)?jiān)谵D(zhuǎn)載時(shí)保留原文鏈接或在文章開(kāi)頭加上本人博客地址,否則保留追究法律責(zé)任的權(quán)利。
總結(jié)
以上是生活随笔為你收集整理的第九节:基于MVC5+AutoFac+EF+Log4Net的基础结构搭建的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 华为鸿蒙OS 3.0系统来了!9款机型可
- 下一篇: 漫威新电影《惊奇少女》豆瓣6.9分:略显