第九節:基於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 : 原創博客請在轉載時保留原文連接或在文章開頭加上本人博客地址,不然保留追究法律責任的權利。
相關文章
相關標籤/搜索