第九節:基於MVC5+AutoFac+EF+Log4Net的基礎結構搭建

一. 前言html

  從本節開始,將陸續的介紹幾種框架搭建組合形式,分析每種搭建形式的優點和弊端,剖析搭建過程當中涉及到的一些思想和技巧。
(一). 技術選型
  1. DotNet框架:4.6
  2. 數據庫訪問:EF 6.2 (CodeFrist模式)
  3. IOC框架:AutoFac 4.8.1 和 AutoFac.MVC5 4.0.2
  4. 日誌框架:log4net 2.0.8
  5. 開發工具:VS2017
(二). 框架目標
  1. 一個項目同時鏈接多個相同種類的數據庫,在一個方法中能夠同時對多個數據進行操做。
  2. 支持多種數據庫:SqlServer、MySQL、Oracle,靈活的切換數據庫。
  3. 抽象成支持多種數據庫鏈接方式:EF、ADO.Net、Dapper。
 

二. 搭建思路mysql

 1. 層次劃分sql

  將框架分爲:Ypf.Data、Ypf.IService、Ypf.Service、Ypf.DTO、Ypf.Utils、Ypf.AdminWeb 六個基本層(後續還會補充 Ypf.Api層),每層的做用分別爲:數據庫

  ①. Ypf.Data:存放鏈接數據庫的相關類,包括EF上下文類、映射的實體類、實體類的FluentApi模式的配置類。編程

  ②. Ypf.IService:業務接口層,用來約束接口規範。架構

  ③. Ypf.Service:業務層,用來編寫整套項目的業務方法,但須要符合Ypf.IService層的接口約束。oracle

  ④. Ypf.DTO: 存放項目中用到的自定義的實體類。app

  ⑤. Ypf.Utils: 工具類框架

  ⑥. Ypf.AdminWeb: 表現層,系統的展現、接受用戶的交互,傳遞數據,業務對接。ide

PS:後續會補充Ypf.Api層,用於接受移動端、或其餘客戶端接口數據,進行相應的業務對接處理。

2. Ypf.Data層的剖析

  把EF封裝到Ypf.Data層,經過Nuget引入EF的程序集,利用FluentAPI的模式先進行配置(實際項目多種模式配合使用),該層的結構以下:

PS:EF的關閉默認策略、EF的DataAnnotations、EF的FluentAPI模式, 在關閉數據庫策略的狀況下,不管哪一種模式都須要顯式的 ToTable來映射表名,不然會提示該類找不到。

EF配置詳情參考:

         第十五節: EF的CodeFirst模式經過DataAnnotations修改默認協定

         第十六節: EF的CodeFirst模式經過Fluent API修改默認協定

3. Service層和IService層簡單的封裝一下

【PS:這個地方是個關鍵點,須要考慮多種不一樣的寫法,而後進行封裝

  ①.【Ypf.Service】層只有一個BaseService泛型類封裝,【Ypf.IService】層並無設置IBaseService接口,設置了一個IServiceSupport標識接口(沒有任何內容),須要「AutoFac注入」的全部子類IxxxService都要實現IServiceSupport接口。

  ②.【Ypf.Service】層中有不少自定義的 xxxService,每一個xxxService都要實現【Ypf.IService】層的IxxxService層接口,這裏的xxxService層劃分並不依賴表名劃分,自定義根據業務合理起名便可。

  ③. xxxService類中,利用using() 包裹EF的上下文「db」便於釋放,而後把EF上下文傳入到泛型的BaseService<T>類的構造函數中,能夠調用其封裝的方法。

  ④.利用AutoFac實如今控制器中屬性的注入,相應的配置均寫在Global文件中。

  ⑤.控制器中的Action僅僅負責傳值和簡單的一些判斷,核心業務所有都寫在Service層中。

4. 利用AutoFac實現Ypf.AdminWeb層與Ypf.Service層解耦

  利用AutoFac進行整合,使Ypf.AdminWeb層只須要引入YpfIService層便可,但須要改一下Ypf. Service輸出路徑使其程序集輸出到Ypf.AdminWeb層中。

 解析:利用AutoFac把Ypf.Service中的全部類註冊給它的所有實現接口(一個類可能實現了多個接口),而且把實現類中的屬性也進行註冊(實現類中也可能包含屬性的注入)。

AutoFac的配置詳情參考: 

         第二節:框架前期準備篇之AutoFac常見用法總結

5. 將Log4net整合到Ypf.Utils層中

  解析:主要配置了兩種模式,輸出到「txt文本文檔」和「SQLServer數據庫中」。其中「文本文檔」又分了兩種模式,所有輸入到一個文檔中 和 不一樣類型的日誌輸入到不一樣文檔下,在調用的時候經過傳入參數來區分存放在哪一個文件夾下。

Log4net的配置詳情參考:

  第一節:框架前期準備篇之Log4Net日誌詳解

6. 完善【Ypf.Service】層中BaseService的封裝

   封裝EF經常使用的增刪改查的方法,這裏暫時先不擴展EF插件的方法,分享一下代碼。

 1 using System;  2 using System.Collections.Generic;  3 using System.Data.Entity;  4 using System.Data.SqlClient;  5 using System.Linq;  6 using System.Linq.Expressions;  7 using System.Reflection;  8 using System.Text;  9 using System.Threading.Tasks;  10 using Ypf.Data;  11 
 12 namespace Ypf.Service.BaseClass  13 {  14     public class BaseService<T> where T : class
 15     //public class BaseService
 16  {  17         private DbContext db;  18 
 19         //子類經過構造函數來傳入EF上下文
 20         public BaseService(DbContext db)  21  {  22             this.db = db;  23  }  24 
 25         /****************************************下面進行方法的封裝***********************************************/
 26         //1. 直接提交數據庫
 27 
 28         #region 01-數據源
 29         public IQueryable<T> Entities  30  {  31             get
 32  {  33                 return db.Set<T>();  34  }  35  }  36         #endregion
 37 
 38         #region 02-新增
 39         public int Add(T model)  40  {  41             DbSet<T> dst = db.Set<T>();  42  dst.Add(model);  43             return db.SaveChanges();  44 
 45  }  46         #endregion
 47 
 48         #region 03-刪除(適用於先查詢後刪除 單個)
 49         /// <summary>
 50         /// 刪除(適用於先查詢後刪除的單個實體)  51         /// </summary>
 52         /// <param name="model">須要刪除的實體</param>
 53         /// <returns></returns>
 54         public int Del(T model)  55  {  56             db.Set<T>().Attach(model);  57             db.Set<T>().Remove(model);  58             return db.SaveChanges();  59  }  60         #endregion
 61 
 62         #region 04-根據條件刪除(支持批量刪除)
 63         /// <summary>
 64         /// 根據條件刪除(支持批量刪除)  65         /// </summary>
 66         /// <param name="delWhere">傳入Lambda表達式(生成表達式目錄樹)</param>
 67         /// <returns></returns>
 68         public int DelBy(Expression<Func<T, bool>> delWhere)  69  {  70             List<T> listDels = db.Set<T>().Where(delWhere).ToList();  71             listDels.ForEach(d =>
 72  {  73                 db.Set<T>().Attach(d);  74                 db.Set<T>().Remove(d);  75  });  76             return db.SaveChanges();  77  }  78         #endregion
 79 
 80         #region 05-單實體修改
 81         /// <summary>
 82         /// 修改  83         /// </summary>
 84         /// <param name="model">修改後的實體</param>
 85         /// <returns></returns>
 86         public int Modify(T model)  87  {  88             db.Entry(model).State = EntityState.Modified;  89             return db.SaveChanges();  90  }  91         #endregion
 92 
 93         #region 06-批量修改(非lambda)
 94         /// <summary>
 95         /// 批量修改(非lambda)  96         /// </summary>
 97         /// <param name="model">要修改實體中 修改後的屬性 </param>
 98         /// <param name="whereLambda">查詢實體的條件</param>
 99         /// <param name="proNames">lambda的形式表示要修改的實體屬性名</param>
100         /// <returns></returns>
101         public int ModifyBy(T model, Expression<Func<T, bool>> whereLambda, params string[] proNames) 102  { 103             List<T> listModifes = db.Set<T>().Where(whereLambda).ToList(); 104             Type t = typeof(T); 105             List<PropertyInfo> proInfos = t.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToList(); 106             Dictionary<string, PropertyInfo> dicPros = new Dictionary<string, PropertyInfo>(); 107             proInfos.ForEach(p =>
108  { 109                 if (proNames.Contains(p.Name)) 110  { 111  dicPros.Add(p.Name, p); 112  } 113  }); 114             foreach (string proName in proNames) 115  { 116                 if (dicPros.ContainsKey(proName)) 117  { 118                     PropertyInfo proInfo = dicPros[proName]; 119                     object newValue = proInfo.GetValue(model, null); 120                     foreach (T m in listModifes) 121  { 122                         proInfo.SetValue(m, newValue, null); 123  } 124  } 125  } 126             return db.SaveChanges(); 127  } 128         #endregion
129 
130         #region 07-根據條件查詢
131         /// <summary>
132         /// 根據條件查詢 133         /// </summary>
134         /// <param name="whereLambda">查詢條件(lambda表達式的形式生成表達式目錄樹)</param>
135         /// <returns></returns>
136         public List<T> GetListBy(Expression<Func<T, bool>> whereLambda) 137  { 138             return db.Set<T>().Where(whereLambda).ToList(); 139  } 140         #endregion
141 
142         #region 08-根據條件排序和查詢
143         /// <summary>
144         /// 根據條件排序和查詢 145         /// </summary>
146         /// <typeparam name="Tkey">排序字段類型</typeparam>
147         /// <param name="whereLambda">查詢條件</param>
148         /// <param name="orderLambda">排序條件</param>
149         /// <param name="isAsc">升序or降序</param>
150         /// <returns></returns>
151         public List<T> GetListBy<Tkey>(Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true) 152  { 153             List<T> list = null; 154             if (isAsc) 155  { 156                 list = db.Set<T>().Where(whereLambda).OrderBy(orderLambda).ToList(); 157  } 158             else
159  { 160                 list = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda).ToList(); 161  } 162             return list; 163  } 164         #endregion
165 
166         #region 09-分頁查詢
167         /// <summary>
168         /// 根據條件排序和查詢 169         /// </summary>
170         /// <typeparam name="Tkey">排序字段類型</typeparam>
171         /// <param name="pageIndex">頁碼</param>
172         /// <param name="pageSize">頁容量</param>
173         /// <param name="whereLambda">查詢條件</param>
174         /// <param name="orderLambda">排序條件</param>
175         /// <param name="isAsc">升序or降序</param>
176         /// <returns></returns>
177         public List<T> GetPageList<Tkey>(int pageIndex, int pageSize, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true) 178  { 179 
180             List<T> list = null; 181             if (isAsc) 182  { 183                 list = db.Set<T>().Where(whereLambda).OrderBy(orderLambda) 184                .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); 185  } 186             else
187  { 188                 list = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda) 189               .Skip((pageIndex - 1) * pageSize).Take(pageSize).ToList(); 190  } 191             return list; 192  } 193         #endregion
194 
195         #region 10-分頁查詢輸出總行數
196         /// <summary>
197         /// 根據條件排序和查詢 198         /// </summary>
199         /// <typeparam name="Tkey">排序字段類型</typeparam>
200         /// <param name="pageIndex">頁碼</param>
201         /// <param name="pageSize">頁容量</param>
202         /// <param name="whereLambda">查詢條件</param>
203         /// <param name="orderLambda">排序條件</param>
204         /// <param name="isAsc">升序or降序</param>
205         /// <returns></returns>
206         public List<T> GetPageList<Tkey>(int pageIndex, int pageSize, ref int rowCount, Expression<Func<T, bool>> whereLambda, Expression<Func<T, Tkey>> orderLambda, bool isAsc = true) 207  { 208             int count = 0; 209             List<T> list = null; 210             count = db.Set<T>().Where(whereLambda).Count(); 211             if (isAsc) 212  { 213                 var iQueryList = db.Set<T>().Where(whereLambda).OrderBy(orderLambda) 214                    .Skip((pageIndex - 1) * pageSize).Take(pageSize); 215 
216                 list = iQueryList.ToList(); 217  } 218             else
219  { 220                 var iQueryList = db.Set<T>().Where(whereLambda).OrderByDescending(orderLambda) 221                  .Skip((pageIndex - 1) * pageSize).Take(pageSize); 222                 list = iQueryList.ToList(); 223  } 224             rowCount = count; 225             return list; 226  } 227         #endregion
228 
229 
230         //2. SaveChange剝離出來,處理事務
231 
232         #region 01-批量處理SaveChange()
233         /// <summary>
234         /// 事務批量處理 235         /// </summary>
236         /// <returns></returns>
237         public int SaveChange() 238  { 239             return db.SaveChanges(); 240  } 241         #endregion
242 
243         #region 02-新增
244         /// <summary>
245         /// 新增 246         /// </summary>
247         /// <param name="model">須要新增的實體</param>
248         public void AddNo(T model) 249  { 250             db.Set<T>().Add(model); 251  } 252         #endregion
253 
254         #region 03-刪除
255         /// <summary>
256         /// 刪除 257         /// </summary>
258         /// <param name="model">須要刪除的實體</param>
259         public void DelNo(T model) 260  { 261             db.Entry(model).State = EntityState.Deleted; 262  } 263         #endregion
264 
265         #region 04-根據條件刪除
266         /// <summary>
267         /// 條件刪除 268         /// </summary>
269         /// <param name="delWhere">須要刪除的條件</param>
270         public void DelByNo(Expression<Func<T, bool>> delWhere) 271  { 272             List<T> listDels = db.Set<T>().Where(delWhere).ToList(); 273             listDels.ForEach(d =>
274  { 275                 db.Set<T>().Attach(d); 276                 db.Set<T>().Remove(d); 277  }); 278  } 279         #endregion
280 
281         #region 05-修改
282         /// <summary>
283         /// 修改 284         /// </summary>
285         /// <param name="model">修改後的實體</param>
286         public void ModifyNo(T model) 287  { 288             db.Entry(model).State = EntityState.Modified; 289  } 290         #endregion
291 
292 
293         //3. EF調用sql語句
294 
295         #region 01-執行增長,刪除,修改操做(或調用存儲過程)
296         /// <summary>
297         /// 執行增長,刪除,修改操做(或調用存儲過程) 298         /// </summary>
299         /// <param name="sql"></param>
300         /// <param name="pars"></param>
301         /// <returns></returns>
302         public int ExecuteSql(string sql, params SqlParameter[] pars) 303  { 304             return db.Database.ExecuteSqlCommand(sql, pars); 305  } 306 
307         #endregion
308 
309         #region 02-執行查詢操做
310         /// <summary>
311         /// 執行查詢操做 312         /// </summary>
313         /// <typeparam name="T"></typeparam>
314         /// <param name="sql"></param>
315         /// <param name="pars"></param>
316         /// <returns></returns>
317         public List<T> ExecuteQuery<T>(string sql, params SqlParameter[] pars) 318  { 319             return db.Database.SqlQuery<T>(sql, pars).ToList(); 320  } 321         #endregion
322 
323 
324 
325  } 326 }
BaseService

 

三. 剖析核心

1. 如何實現同時操做多個相同類型的不一樣結構的數據庫。

  首先【Ypf.Data】層中新建一個存放的實體的文件夾,如「EntityTest」,用來引入另一個數據庫的存放實體和DbContext上下文,而後在【Ypf.Service】層中,雙Using,往BaseService類中傳入不一樣db上下文便可實現訪問不一樣的數據庫,若是要對多個數據庫開啓事務,手動開啓msdtc服務,而後使用Transactions包裹,進行事務一體操做。

  詳細的使用步驟見:實戰測試。

2. 體會【Ypf.IService】層 和 引入IOC框架的做用

【PS:依賴倒置原則的核心就是:面向接口編程】

(1). 接口層的做用:

  a. 便於開發人員分工開發,寫業務的單獨去寫業務,對接的單獨去對接,並且事先把接口協議定好,那麼對接的人員就不須要等業務人員所有寫完代碼,就能夠對接了,無非最後再測試而已。

  b. 下降修改代碼形成的成本代價,使以接口爲基礎搭建起來的框架更加穩健。

舉例1: 三層架構 數據庫訪問層、業務邏輯層、UI調用層。 (非此套框架的模式,後面考慮這麼改進)

①. 數據庫訪問層中有一個 MySqlHelp類,提供連接MySQL數據增刪改查的方法。

②. 業務邏輯層有一個登陸業務 CheckLogin(MySqlHelp mysql,string userName,string pwd)。

③. UI調用層要調用CheckLogin方法,這時候實例化一個MySqlHelp對象,傳到CheckLogin方法中便可。

  有一個天,要求支持oracle數據庫,因此數據庫訪問層中增長了一個oracleHelper類,UI調用層按照常規實例化了一個oracleHelper對象,傳到CheckLogin方法中,發現個人天!!!!CheckLogin居然不支持oracleHelper對象,同時發現相似的全部業務層的方法都不支持oracleHelper類,這個時候悲劇就發生了,若是所有改業務層的方法,基本上完蛋。

因此根本的解決方案:依賴倒置原則,即面向接口編程。

①. 數據庫訪問層聲明一個接口IHelper,裏面有增刪改查方法,MySqlHelp和oracleHelper都實現IHelper接口。

②. 業務邏輯層有一個登陸業務改成依賴接口IHelper, CheckLogin(IHelper iHelper,string userName,string pwd)。

③. UI調用層要調用CheckLogin方法,想連哪一個數據,就實例化哪一個 eg IHelper iHelper=new MySqlHelp(); 或者 IHelper iHelper=new oracleHelper(),此處考慮和IOC框架結合,連代碼都不用改,直接改配置文件就好了,就能夠切換實例,而後調用CheckLogin便可。

舉例2: 類A,類B,類C。

類A中的方法須要傳入類B的實例,一般在類A中實例化一下類B,但若是想讓類A依賴類C,你會發現改動很是大,類A中的方法原先是類B的參數所有須要改。

因此解決方案:類B和類C都實現接口I,類A中方法的參數由原先的類B改成接口I,這樣類A想依賴誰,只須要 I i=new B() 或者 I i=new C(),全部的方法都不用改,也能夠再升級一下,這裏不直接實例化,利用IOC框架或者手寫反射,只須要改一下配置文件,就能控制 究竟是 new B 仍是 new C 。

 (2). 引入IOC框架的做用:

  解決的問題1:現有的框架模式(Service層using引入EF上下文,傳入到BaseService類中),如何實現快速切換數據庫?

  a.首先在【Ypf.Data】層引入MySQL數據庫所須要的程序集,配置文件也改爲鏈接MySQL的。(此處須要詳細測試)

  b. 新建一個【Ypf.Service2】層,一樣實現對應業務,只不過是鏈接不一樣類型的數據庫(好比它鏈接的是MySql數據庫),生成路徑也輸出到【Ypf.AdminWeb】層中,最後只須要改一下AutoFac讀取的配置文件「DllName」改成「Ypf.Services2」便可,就能夠實現切換數據。

  總結:該模式雖然能實現「相同業務、相同表」的不一樣類型的數據庫切換(好比SQLServer→MySQL),可是須要從新寫一個整層【Ypf.Service2】,雖然基本上是複製,可是有必定工做量的。可是另外經過手寫IOC也能夠實現(反射+簡單工廠+配置文件),看不到IOC框架的優點所在。

  IOC強大之處在於框架自己爲咱們封裝好了不少便於開發的方法,拿AutoFac來講吧,能靈活的控制建立對象的(每次請求都建立、單例、一個Http請求內單例)

四. 實戰測試

這裏準備兩個數據庫,分別是:YpfFrame_DB 和 YpfFrameTest_DB

①:YpfFrame_DB中,用到了表:T_SysUser 和 T_SysLoginLog,表結構以下

②. YpfFrameTest_DB 表中用到了T_SchoolInfor,表結構以下

開始測試

1. 測試增刪改查,包括基本的事務一體。

在【Ypf.IService】層中新建ITestService接口,在【Ypf.Service】層中新建TestService類,實現ITestService接口, 定義TestBasicCRUD方法,進行測試,代碼以下。

 1         /// <summary>
 2         /// 1.測試基本的增刪改查,事務一體  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. 測試一個方法中查詢多個數據庫。

在ITestService接口中定義ConnectManyDB方法,並在TestService中實現該方法,代碼以下:

 1         /// <summary>
 2         /// 2. 同時鏈接多個數據庫進行  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                 //執行數據庫查詢操做
15                 userList = T_SysUserService.GetListBy(u => true); 16                 schoolList = T_SchoolInforService.GetListBy(u => true); 17  } 18         }

  分析:想鏈接幾個數據庫,就須要先在【Ypf.Data】層中新建對應數據庫的實體、實體配置文件、EF上下文,而後在【Ypf.Service】層對應的方法中實例化對應的 EF上下文,而後傳入到BaseService類中便可。

3. 測試一個方法中事務一體處理多個數據庫的crud操做。

 在ITestService接口中定義ManyDBTransaction方法,並在TestService中實現該方法,代碼以下:

 1         /// <summary>
 2         /// 3. 同時對多個數據庫進行事務一體的CRUD操做  3         /// 注:須要手動開啓msdtc服務(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                     //執行業務操做
18                     T_SysUserService.DelBy(u => u.id == "1"); 19                     T_SchoolInforService.DelBy(u => u.id == "1"); 20 
21                     //最終提交事務
22  trans.Complete(); 23  } 24                 catch (Exception ex) 25  { 26                     var msg = ex.Message; 27                     //事務回滾
28  Transaction.Current.Rollback(); 29                     throw; 30  } 31  } 32         }

  分析:同時鏈接多個數據庫,並對多個數據庫進行事務性的crud操做,這個時候必須用 【TransactionScope事務】,前提要手動 【net start msdtc 】開啓對應服務,這樣整個事務經過「Complete」方法進行提交,經過Transaction.Current.Rollback()方法進行事務回滾,各自db的SaveChange不起做用,但仍是須要SaveChange的。

4. 測試xxxSevice子類中也能夠經過AutoFac進行IxxxService的模式進行屬性的注入。

 在【Ypf.IService】層中新建ITestService2接口,在【Ypf.Service】層中新建TestService2類,實現ITestService接口, 定義GetUserInfor方法,進行測試,代碼以下。

 1  public class TestService2 : ITestService2  2  {  3         /// <summary>
 4         /// 獲取用戶信息  5         /// </summary>
 6         /// <returns></returns>
 7         public List<T_SysUser> GetUserInfor()  8  {  9             using (DbContext db=new MyDBContext1()) 10  { 11                 BaseService<T_SysUser> T_SysUserService = new BaseService<T_SysUser>(db); 12                 return T_SysUserService.GetListBy(u => true); 13  } 14  } 15     }
View Code

在TestService中定義ITestService2屬性,以下:

在TestService中定義以下方法,內部用TestService2進行調用,能夠調用成功,從而證實xxxSevice子類中也能夠經過AutoFac進行IxxxService的模式進行屬性的注入

5. 測試Log4net的分文件夾和不分文件的使用。

 先分享配置文件:

 1 <?xml version="1.0" encoding="utf-8" ?>
 2 <configuration>
 3   <!-- 一. 添加log4net的自定義配置節點-->
 4   <configSections>
 5     <section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler,log4net" />
 6   </configSections>
 7   <!--二. log4net的核心配置代碼-->
 8   <log4net>
 9     <!--1. 輸出途徑(一) 將日誌以回滾文件的形式寫到文件中-->
 10     
 11     <!--模式一:所有存放到一個文件夾裏-->
 12     <appender name="log0" type="log4net.Appender.RollingFileAppender">
 13       <!--1.1 文件夾的位置(也能夠寫相對路徑)-->
 14       <param name="File" value="D:\MyLog\" />
 15       <!--相對路徑-->
 16       <!--<param name="File" value="Logs/" />-->
 17       <!--1.2 是否追加到文件-->
 18       <param name="AppendToFile" value="true" />
 19       <!--1.3 使用最小鎖定模型(minimal locking model),以容許多個進程能夠寫入同一個文件 -->
 20       <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
 21       <!--1.4 配置Unicode編碼-->
 22       <Encoding value="UTF-8" />
 23       <!--1.5 是否只寫到一個文件裏-->
 24       <param name="StaticLogFileName" value="false" />
 25       <!--1.6 配置按照何種方式產生多個日誌文件 (Date:日期、Size:文件大小、Composite:日期和文件大小的混合方式)-->
 26       <param name="RollingStyle" value="Composite" />
 27       <!--1.7 介紹多種日誌的的命名和存放在磁盤的形式-->
 28       <!--1.7.1 在根目錄下直接以日期命名txt文件 注意&quot;的位置,去空格 -->
 29       <param name="DatePattern" value="yyyy-MM-dd&quot;.log&quot;" />
 30       <!--1.7.2 在根目錄下按日期產生文件夾,文件名固定 test.log -->
 31       <!--<param name="DatePattern" value="yyyy-MM-dd/&quot;test.log&quot;" />-->
 32       <!--1.7.3 在根目錄下按日期產生文件夾,這是按日期產生文件夾,並在文件名前也加上日期 -->
 33       <!--<param name="DatePattern" value="yyyyMMdd/yyyyMMdd&quot;-test.log&quot;" />-->
 34       <!--1.7.4 在根目錄下按日期產生文件夾,這再造成下一級固定的文件夾 -->
 35       <!--<param name="DatePattern" value="yyyyMMdd/&quot;OrderInfor/test.log&quot;" />-->
 36       <!--1.8 配置每一個日誌的大小。【只在1.6 RollingStyle 選擇混合方式與文件大小方式下才起做用!!!】可用的單位:KB|MB|GB。不要使用小數,不然會一直寫入當前日誌,  37  超出大小後在全部文件名後自動增長正整數從新命名,數字最大的最先寫入。-->
 38       <param name="maximumFileSize" value="10MB" />
 39       <!--1.9 最多產生的日誌文件個數,超過則保留最新的n個 將value的值設置-1,則不限文件個數 【只在1.6 RollingStyle 選擇混合方式與文件大小方式下才起做用!!!】  40  與1.8中maximumFileSize文件大小是配合使用的-->
 41       <param name="MaxSizeRollBackups" value="5" />
 42       <!--1.10 配置文件文件的佈局格式,使用PatternLayout,自定義佈局-->
 43       <layout type="log4net.Layout.PatternLayout">
 44         <conversionPattern value="記錄時間:%date %n線程ID:[%thread] %n日誌級別:%-5level %n出錯類:%logger property: [%property{NDC}] - %n錯誤描述:%message%newline %n%newline"/>
 45       </layout>
 46     </appender>
 47 
 48     <!--模式二:分文件夾存放-->
 49     <!--文件夾1-->
 50     <appender name="log1" type="log4net.Appender.RollingFileAppender">
 51       <param name="File" value="D:\MyLog\OneLog\" />
 52       <param name="AppendToFile" value="true" />
 53       <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
 54       <Encoding value="UTF-8" />
 55       <param name="StaticLogFileName" value="false" />
 56       <param name="RollingStyle" value="Composite" />
 57       <param name="DatePattern" value="yyyy-MM-dd&quot;.log&quot;" />
 58       <param name="maximumFileSize" value="10MB" />
 59       <param name="MaxSizeRollBackups" value="5" />
 60       <layout type="log4net.Layout.PatternLayout">
 61         <conversionPattern value="%message%newline" />
 62       </layout>
 63       <!--下面是利用過濾器進行分文件夾存放,兩種過濾器進行配合-->
 64       <!--與Logger名稱(OneLog)匹配,才記錄,-->
 65       <filter type="log4net.Filter.LoggerMatchFilter">
 66         <loggerToMatch value="OneLog" />
 67       </filter>
 68       <!--阻止全部的日誌事件被記錄-->
 69       <filter type="log4net.Filter.DenyAllFilter" />
 70     </appender>
 71     <!--文件夾2-->
 72     <appender name="log2" type="log4net.Appender.RollingFileAppender">
 73       <param name="File" value="D:\MyLog\TwoLog\" />
 74       <param name="AppendToFile" value="true" />
 75       <lockingModel type="log4net.Appender.FileAppender+MinimalLock" />
 76       <Encoding value="UTF-8" />
 77       <param name="StaticLogFileName" value="false" />
 78       <param name="RollingStyle" value="Composite" />
 79       <param name="DatePattern" value="yyyy-MM-dd&quot;.log&quot;" />
 80       <param name="maximumFileSize" value="10MB" />
 81       <param name="MaxSizeRollBackups" value="5" />
 82       <layout type="log4net.Layout.PatternLayout">
 83         <conversionPattern value="%message%newline" />
 84       </layout>
 85       <!--下面是利用過濾器進行分文件夾存放,兩種過濾器進行配合-->
 86       <!--與Logger名稱(TwoLog)匹配,才記錄,-->
 87       <filter type="log4net.Filter.LoggerMatchFilter">
 88         <loggerToMatch value="TwoLog" />
 89       </filter>
 90       <!--阻止全部的日誌事件被記錄-->
 91       <filter type="log4net.Filter.DenyAllFilter" />
 92     </appender>
 93 
 94 
 95     <!--2. 輸出途徑(二) 記錄日誌到數據庫-->
 96     <appender name="AdoNetAppender" type="log4net.Appender.AdoNetAppender">
 97       <!--2.1 設置緩衝區大小,只有日誌記錄超設定值纔會一塊寫入到數據庫-->
 98       <param name="BufferSize" value="1" />
 99       <!--2.2 引用-->
100       <connectionType value="System.Data.SqlClient.SqlConnection, System.Data, Version=1.0.3300.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
101       <!--2.3 數據庫鏈接字符串-->
102       <connectionString value="data source=localhost;initial catalog=LogDB;integrated security=false;persist security info=True;User ID=sa;Password=123456" />
103       <!--2.4 SQL語句插入到指定表-->
104       <commandText value="INSERT INTO LogInfor ([threadId],[log_level],[log_name],[log_msg],[log_exception],[log_time]) VALUES (@threadId, @log_level, @log_name, @log_msg, @log_exception,@log_time)" />
105       <!--2.5 數據庫字段匹配-->
106       <!-- 線程號-->
107       <parameter>
108         <parameterName value="@threadId" />
109         <dbType value="String" />
110         <size value="100" />
111         <layout type="log4net.Layout.PatternLayout">
112           <conversionPattern value="%thread" />
113         </layout>
114       </parameter>
115       <!--日誌級別-->
116       <parameter>
117         <parameterName value="@log_level" />
118         <dbType value="String" />
119         <size value="100" />
120         <layout type="log4net.Layout.PatternLayout">
121           <conversionPattern value="%level" />
122         </layout>
123       </parameter>
124       <!--日誌記錄類名稱-->
125       <parameter>
126         <parameterName value="@log_name" />
127         <dbType value="String" />
128         <size value="100" />
129         <layout type="log4net.Layout.PatternLayout">
130           <conversionPattern value="%logger" />
131         </layout>
132       </parameter>
133       <!--日誌信息-->
134       <parameter>
135         <parameterName value="@log_msg" />
136         <dbType value="String" />
137         <size value="5000" />
138         <layout type="log4net.Layout.PatternLayout">
139           <conversionPattern value="%message" />
140         </layout>
141       </parameter>
142       <!--異常信息 指的是如Infor 方法的第二個參數的值-->
143       <parameter>
144         <parameterName value="@log_exception" />
145         <dbType value="String" />
146         <size value="2000" />
147         <layout type="log4net.Layout.ExceptionLayout" />
148       </parameter>
149       <!-- 日誌記錄時間-->
150       <parameter>
151         <parameterName value="@log_time" />
152         <dbType value="DateTime" />
153         <layout type="log4net.Layout.RawTimeStampLayout" />
154       </parameter>
155     </appender>
156 
157 
158     <!--(二). 配置日誌的的輸出級別和加載日誌的輸出途徑-->
159     <root>
160       <!--1. level中的value值表示該值及其以上的日誌級別纔會輸出-->
161       <!--OFF > FATAL(致命錯誤) > ERROR(通常錯誤) > WARN(警告) > INFO(通常信息) > DEBUG(調試信息) > ALL -->
162       <!--OFF表示全部信息都不寫入,ALL表示全部信息都寫入-->
163       <level value="ALL"></level>
164       <!--2. append-ref標籤表示要加載前面的日誌輸出途徑代碼 經過ref和appender標籤的中name屬性相關聯-->
165       
166       <appender-ref ref="log0"></appender-ref>
167       <appender-ref ref="log1"></appender-ref>
168       <appender-ref ref="log2"></appender-ref>
169 
170       <!--<appender-ref ref="AdoNetAppender"></appender-ref>-->
171     </root>
172   </log4net>
173 
174 </configuration>
View Code

分享對應的封裝類:

 1 using log4net;  2 using System;  3 using System.Collections.Generic;  4 using System.Diagnostics;  5 using System.Linq;  6 using System.Reflection;  7 using System.Text;  8 using System.Threading.Tasks;  9 
 10 namespace Ypf.Utils.Log  11 {  12     public class LogUtils  13  {  14         //聲明文件夾名稱(這裏分兩個文件夾)
 15         static string log1Name = "OneLog";  16         static string log2Name = "TwoLog";  17 
 18         //能夠聲明多個日誌對象  19         //模式一:不分文件夾(全部的log對存放在這一個文件夾下)
 20         public static ILog log = LogManager.GetLogger(typeof(LogUtils));  21 
 22         //模式二:分文件夾  23         //若是是要分文件夾存儲,這裏的名稱須要和配置文件中loggerToMatch節點中的value相配合  24         //1. OneLog文件夾
 25         public static ILog log1 = LogManager.GetLogger(log1Name);  26         //2. TwoLog文件夾
 27         public static ILog log2 = LogManager.GetLogger(log2Name);  28 
 29         #region 01-初始化Log4net的配置
 30         /// <summary>
 31         /// 初始化Log4net的配置  32         /// xml文件必定要改成嵌入的資源  33         /// </summary>
 34         public static void InitLog4Net()  35  {  36             Assembly assembly = Assembly.GetExecutingAssembly();  37             var xml = assembly.GetManifestResourceStream("Ypf.Utils.Log.log4net.xml");  38  log4net.Config.XmlConfigurator.Configure(xml);  39  }  40         #endregion
 41 
 42         /************************* 五種不一樣日誌級別 *******************************/
 43         //FATAL(致命錯誤) > ERROR(通常錯誤) > WARN(警告) > INFO(通常信息) > DEBUG(調試信息)
 44 
 45         #region 00-將調試的信息輸出,能夠定位到具體的位置(解決高層封裝帶來的問題)
 46         /// <summary>
 47         /// 將調試的信息輸出,能夠定位到具體的位置(解決高層封裝帶來的問題)  48         /// </summary>
 49         /// <returns></returns>
 50         private static string getDebugInfo()  51  {  52             StackTrace trace = new StackTrace(true);  53             return trace.ToString();  54  }  55         #endregion
 56 
 57         #region 01-DEBUG(調試信息)
 58         /// <summary>
 59         /// DEBUG(調試信息)  60         /// </summary>
 61         /// <param name="msg">日誌信息</param>
 62         ///  <param name="logName">文件夾名稱</param>
 63         public static void Debug(string msg, string logName = "")  64  {  65             if (logName == "")  66  {  67                 log.Debug(getDebugInfo() + msg);  68  }  69             else if (logName == log1Name)  70  {  71  log1.Debug(msg);  72  }  73             else if (logName == log2Name)  74  {  75  log2.Debug(msg);  76  }  77  }  78         /// <summary>
 79         /// Debug  80         /// </summary>
 81         /// <param name="msg">日誌信息</param>
 82         /// <param name="exception">錯誤信息</param>
 83         public static void Debug(string msg, Exception exception)  84  {  85             log.Debug(getDebugInfo() + msg, exception);  86  }  87 
 88         #endregion
 89 
 90         #region 02-INFO(通常信息)
 91         /// <summary>
 92         /// INFO(通常信息)  93         /// </summary>
 94         /// <param name="msg">日誌信息</param>
 95         /// <param name="logName">文件夾名稱</param>
 96         public static void Info(string msg, string logName = "")  97  {  98             if (logName == "")  99  { 100                 log.Info(getDebugInfo() + msg); 101  } 102             else if (logName == log1Name) 103  { 104  log1.Info(msg); 105  } 106             else if (logName == log2Name) 107  { 108  log2.Info(msg); 109  } 110  } 111         /// <summary>
112         /// Info 113         /// </summary>
114         /// <param name="msg">日誌信息</param>
115         /// <param name="exception">錯誤信息</param>
116         public static void Info(string msg, Exception exception) 117  { 118             log.Info(getDebugInfo() + msg, exception); 119  } 120         #endregion
121 
122         #region 03-WARN(警告)
123         /// <summary>
124         ///WARN(警告) 125         /// </summary>
126         /// <param name="msg">日誌信息</param>
127         /// <param name="logName">文件夾名稱</param>
128         public static void Warn(string msg, string logName = "") 129  { 130             if (logName == "") 131  { 132                 log.Warn(getDebugInfo() + msg); 133  } 134             else if (logName == log1Name) 135  { 136  log1.Warn(msg); 137  } 138             else if (logName == log2Name) 139  { 140  log2.Warn(msg); 141  } 142  } 143         /// <summary>
144         /// Warn 145         /// </summary>
146         /// <param name="msg">日誌信息</param>
147         /// <param name="exception">錯誤信息</param>
148         public static void Warn(string msg, Exception exception) 149  { 150             log.Warn(getDebugInfo() + msg, exception); 151  } 152         #endregion
153 
154         #region 04-ERROR(通常錯誤)
155         /// <summary>
156         /// ERROR(通常錯誤) 157         /// </summary>
158         /// <param name="msg">日誌信息</param>
159         /// <param name="logName">文件夾名稱</param>
160         public static void Error(string msg, string logName = "") 161  { 162             if (logName == "") 163  { 164                 log.Error(getDebugInfo() + msg); 165  } 166             else if (logName == log1Name) 167  { 168  log1.Error(msg); 169  } 170             else if (logName == log2Name) 171  { 172  log2.Error(msg); 173  } 174  } 175         /// <summary>
176         /// Error 177         /// </summary>
178         /// <param name="msg">日誌信息</param>
179         /// <param name="exception">錯誤信息</param>
180         public static void Error(string msg, Exception exception) 181  { 182             log.Error(getDebugInfo() + msg, exception); 183  } 184         #endregion
185 
186         #region 05-FATAL(致命錯誤)
187         /// <summary>
188         /// FATAL(致命錯誤) 189         /// </summary>
190         /// <param name="msg">日誌信息</param>
191         /// <param name="logName">文件夾名稱</param>
192         public static void Fatal(string msg, string logName = "") 193  { 194             if (logName == "") 195  { 196                 log.Fatal(getDebugInfo() + msg); 197  } 198             else if (logName == log1Name) 199  { 200  log1.Fatal(msg); 201  } 202             else if (logName == log2Name) 203  { 204  log2.Fatal(msg); 205  } 206  } 207         /// <summary>
208         /// Fatal 209         /// </summary>
210         /// <param name="msg">日誌信息</param>
211         /// <param name="exception">錯誤信息</param>
212         public static void Fatal(string msg, Exception exception) 213  { 214             log.Fatal(getDebugInfo() + msg, exception); 215  } 216 
217         #endregion
218 
219 
220 
221  } 222 }
View Code

代碼測試:

 

 

 

 

 

!

  • 做       者 : Yaopengfei(姚鵬飛)
  • 博客地址 : http://www.cnblogs.com/yaopengfei/
  • 聲     明1 : 本人才疏學淺,用郭德綱的話說「我是一個小學生」,若有錯誤,歡迎討論,請勿謾罵^_^。
  • 聲     明2 : 原創博客請在轉載時保留原文連接或在文章開頭加上本人博客地址,不然保留追究法律責任的權利。
相關文章
相關標籤/搜索