第九节:基于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 : 原创博客请在转载时保留原文连接或在文章开头加上本人博客地址,不然保留追究法律责任的权利。