从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

一、回顾系统进度以及本章概要

目前博客系统已经数据库创建、以及依赖注入Autofac集成,接下来就是日志和缓存集成,这里日志用的是Nlog,其实还有其他的日志框架如log4,这些博客园都有很多介绍,这里就不说了,缓存机制用的是微软自带的MemoryCache和比较流行Redis,这里我也只是了解使用,没有做更升入的研究,以后好好学一下Redis,然后就是实现一个BaseController父类用来重写JsonResult方法为的是返回时间格式问题,默认json返回的时间格式是Date(84923838332223)转为常见的yyyy-MM-dd HH:mm:ss格式。

二、缓存机制实现

1、在公共程序集中创建连个文件加一个Cache用来存放缓存类,一个是Log是用来创建Nlog类,这里都使用接口来实现,以便可以以后可以多个实现。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

2、首先创建一个ICacheManager接口类。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.Common.Cache
2 {
3 public interface ICacheManager
4 {
5 /// <summary>
6 /// 获取
7 /// </summary>
8 /// <typeparam name="TEntity"></typeparam>
9 /// <param name="key"></param>
10 /// <returns></returns>
11 TEntity Get<TEntity>(string key);
12 //设置
13 void Set(string key, object value, TimeSpan cacheTime);
14 //判断是否存在
15 bool Contains(string key);
16 //移除
17 void Remove(string key);
18 //清除
19 void Clear();
20
21 }
22 }

3、在实现微软缓存机制的时候需要引用System.Runtime.Caching.dll,创建一个MemoryCacheManager 类

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.Common.Cache
2 {
3 public class MemoryCacheManager : ICacheManager
4 {
5 public void Clear()
6 {
7
8 foreach (var item in MemoryCache.Default)
9 {
10 this.Remove(item.Key);
11 }
12 }
13
14 public bool Contains(string key)
15 {
16 return MemoryCache.Default.Contains(key);
17 }
18
19 public TEntity Get<TEntity>(string key)
20 {
21 return (TEntity)MemoryCache.Default.Get(key);
22 }
23
24 public void Remove(string key)
25 {
26 MemoryCache.Default.Remove(key);
27 }
28
29 public void Set(string key, object value, TimeSpan cacheTime)
30 {
31 MemoryCache.Default.Add(key, value, new CacheItemPolicy { SlidingExpiration = cacheTime });
32 }
33 }
34 }

4、实现RedisCacheManager类,这里我们使用的免费的Redis客服端是StackExchange.Redis.可以在nuget中下载到。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

RedisCacheManager类

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.Common.Cache
2 {
3 public class RedisCacheManager : ICacheManager
4 {
5 private readonly string redisConnenctionString;
6
7 public volatile ConnectionMultiplexer redisConnection;
8
9 private readonly object redisConnectionLock = new object();
10
11 public RedisCacheManager()
12 {
13 //链接redis服务语句
14 string redisConfiguration = ConfigurationManager.ConnectionStrings["redisCache"].ToString();
15
16 if (string.IsNullOrWhiteSpace(redisConfiguration))
17 {
18 throw new ArgumentException("redis config is empty", nameof(redisConfiguration));
19 }
20 this.redisConnenctionString = redisConfiguration;
21 this.redisConnection = GetRedisConnection();
22 }
23
24 private ConnectionMultiplexer GetRedisConnection()
25 {
26 if (this.redisConnection != null && this.redisConnection.IsConnected)
27 {
28 return this.redisConnection;
29 }
30 lock (redisConnectionLock)
31 {
32 if (this.redisConnection != null)
33 {
34 //释放redis连接
35 this.redisConnection.Dispose();
36 }
37 this.redisConnection = ConnectionMultiplexer.Connect(redisConnenctionString);
38 }
39 return this.redisConnection;
40 }
41
42 public void Clear()
43 {
44 foreach (var endPoint in this.GetRedisConnection().GetEndPoints())
45 {
46 var server = this.GetRedisConnection().GetServer(endPoint);
47 foreach (var key in server.Keys())
48 {
49 redisConnection.GetDatabase().KeyDelete(key);
50 }
51 }
52 }
53
54 public bool Contains(string key)
55 {
56 return redisConnection.GetDatabase().KeyExists(key);
57 }
58
59 public TEntity Get<TEntity>(string key)
60 {
61 var value = redisConnection.GetDatabase().StringGet(key);
62 if (value.HasValue)
63 {
64 return SerializeHelper.Deserialize<TEntity>(value);
65 } else
66 {
67 return default(TEntity);
68 }
69 }
70
71 public void Remove(string key)
72 {
73 redisConnection.GetDatabase().KeyDelete(key);
74 }
75
76 public void Set(string key, object value, TimeSpan cacheTime)
77 {
78 if (value != null)
79 {
80 redisConnection.GetDatabase().StringSet(key, SerializeHelper.Serialize(value), cacheTime);
81 }
82 }
83 }
84 }

这里在存储数据的时候使用到了序列化和反序列化,用的序列化工具是Newtonsoft.Json,同样也可以在nuget中找到。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

SerializeHelper序列化帮助类

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.Common
2 {
3 public class SerializeHelper
4 {
5 /// <summary>
6 /// 序列化
7 /// </summary>
8 /// <param name="item"></param>
9 /// <returns></returns>
10 public static byte[] Serialize(object item)
11 {
12 var jsonString = JsonConvert.SerializeObject(item);
13
14 return Encoding.UTF8.GetBytes(jsonString);
15 }
16 /// <summary>
17 /// 反序列化
18 /// </summary>
19 /// <typeparam name="TEntity"></typeparam>
20 /// <param name="value"></param>
21 /// <returns></returns>
22 public static TEntity Deserialize<TEntity>(byte[] value)
23 {
24 if (value == null)
25 {
26 return default(TEntity);
27 }
28 var jsonString = Encoding.UTF8.GetString(value);
29 return JsonConvert.DeserializeObject<TEntity>(jsonString);
30 }
31 }
32 }

三、日志处理:Nlog日志框架

1、首先实现一个日子接口ILogger

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.Common.Log
2 {
3 public interface ILogger
4 {
5 void Debug(string message);
6 void Debug(string message, Exception exception);
7 void Error(string message);
8 void Error(string message, Exception exception);
9 void Fatal(string message);
10 void Fatal(string message, Exception exception);
11 void Info(string message);
12 void Info(string message, Exception exception);
13 void Warn(string message);
14 void Warn(string message, Exception exception);
15 }
16 }

2.在nuget中添加Nlog框架

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

nlog.config是日志框架的配置文件。

Nloglogger类

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.Common.Log
2 {
3 public class NLogLogger : ILogger
4 {
5 private readonly Logger logger = LogManager.GetCurrentClassLogger();
6 public void Debug(string message)
7 {
8 logger.Debug(message);
9 }
10
11 public void Debug(string message, Exception exception)
12 {
13 logger.Debug(exception, message);
14 }
15
16 public void Error(string message)
17 {
18 logger.Error(message);
19 }
20
21 public void Error(string message, Exception exception)
22 {
23 logger.Error(exception, message);
24 }
25
26 public void Fatal(string message)
27 {
28 logger.Fatal(message);
29 }
30
31 public void Fatal(string message, Exception exception)
32 {
33 logger.Fatal(exception, message);
34 }
35
36 public void Info(string message)
37 {
38 logger.Info(message);
39 }
40
41 public void Info(string message, Exception exception)
42 {
43 logger.Info(exception, message);
44 }
45
46 public void Warn(string message)
47 {
48 logger.Warn(message);
49 }
50
51 public void Warn(string message, Exception exception)
52 {
53 logger.Warn(exception, message);
54 }
55 }
56 }

3、配置日志文件NLog.config,这里是在webUI层应用这个文件,因为最终日志是在web下运行。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

在targets的节点下面配置,这里是以文件的方式保存日子,你也可以使用这个配置一个直接把日子写到数据库中

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 <target xsi:type ="File"
2 name="file"
3 header="------------------------------Start------------------------------"
4 footer="------------------------------End------------------------------"
5 fileName="${basedir}/App_Data/Logs/${shortdate}.log"
6 layout="${longdate} - ${level:uppercase=true}:${message} ${callsite:fileName=true} ${exception:format=Type,Message,Method,StackTrace:maxInnerExceptionLevel=5:innerFormat=ShortType,Message,Method,StackTrace}"
7 keepFileOpen="false"
8 archiveFileName="${basedir}/App_Data/Logs/Backup_${shortdate}.{##}.log"
9 archiveNumbering="Sequence"
10 archiveEvery="Day"
11 maxArchiveFiles="30">
12
13 </target>

在rules节点下配置 <logger name="*" minlevel="Error" writeTo="file" />表示什么级别的日志对应放在哪个配置里面。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

这里日志保存在发布站点App_Data\Logs下

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

4、日志测试

4.1在测试之前首先设置一个全局错误机制文件ExpFilter继承HandleErrorAttribute,放在Webcore下面

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

这里需要添加System.Web.Mvc.dll程序集。

ExpFilter类:

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.WebCore
2 {
3 public class ExpFilter:HandleErrorAttribute
4 {
5 public override void OnException(ExceptionContext filterContext)
6 {
7 Exception exp = filterContext.Exception;
8
9 //获取ex的第一级内部异常
10 Exception innerEx = exp.InnerException == null ? exp : exp.InnerException;
11 //循环获取内部异常直到获取详细异常信息为止
12 while (innerEx.InnerException!=null)
13 {
14 innerEx = innerEx.InnerException;
15 }
16 NLogLogger nlog = new NLogLogger();
17 if (filterContext.HttpContext.Request.IsAjaxRequest())
18 {
19
20 nlog.Error(innerEx.Message);
21 JsonConvert.SerializeObject(new { status = 1, msg ="请求发生错误,请联系管理员"});
22 }
23 else
24 {
25 nlog.Error("Error",exp);
26 ViewResult vireResult = new ViewResult();
27 vireResult.ViewName = "/Views/Shared/Error.cshtml";
28 filterContext.Result = vireResult;
29 }
30
31 //告诉MVC框架异常被处理
32 filterContext.ExceptionHandled = true;
33 base.OnException(filterContext);
34 }
35 }
36 }
37

4.2这里对两种请求方式做处理一种是Ajax请求,一种是对链接地址做处理,另外还需要在webui下创建一个错误提醒页面。(/Views/Shared/Error.cshtml)

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

4.3在homecontroller控制器下写错误代码

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

4.4日志测试结果:这里直接开始执行(不调试)

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

然后在项目文件下查看web站点下的\App_Data\Logs查看日子文件

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

日志信息:错误信息,以及错误是那个文件多少行都有显示。

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

四、创建BaseController类

这里使用反序列化工具都是Newtonsoft.Json

BaseController类:

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.WebCore
2 {
3 public class BaseController: Controller
4 {
5 protected override JsonResult Json(object data, string contentType, Encoding contentEncoding, JsonRequestBehavior behavior)
6 {
7 return new JsonNetResult { Data = data, ContentType = contentType, ContentEncoding = contentEncoding, JsonRequestBehavior = behavior };
8 }
9 }
10 }

JsonNetResult类:

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController
  1 namespace Wchl.WMBlog.WebCore
2 {
3 public class JsonNetResult:JsonResult
4 {
5 public override void ExecuteResult(ControllerContext context)
6 {
7 if (context==null)
8 {
9 throw new ArgumentException(nameof(context));
10 }
11
12 var response = context.HttpContext.Response;
13
14 response.ContentType = !string.IsNullOrEmpty(ContentType) ? ContentType : "application/json";
15
16 if (ContentEncoding != null)
17 {
18 response.ContentEncoding = ContentEncoding;
19 }
20
21 var jsonSerializerSetting = new JsonSerializerSettings();
22 //首字母小写
23 jsonSerializerSetting.ContractResolver = new CamelCasePropertyNamesContractResolver();
24 //日期格式化
25 jsonSerializerSetting.DateFormatString = "yyyy-MM-dd HH:mm:ss";
26 var json = JsonConvert.SerializeObject(Data, Formatting.None, jsonSerializerSetting);
27
28 response.Write(json);
29
30 }
31 }
32 }

直接在创建的控制器下集成:

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

接下来就是准备实现页面布局,先做个简单的前台查看,后台分布的功能,然后在一步一步的完善,希望大家多多指点,多多支持,谢谢了。

10月10日补充说明:

全局错误处理机制需要在FilterConfig中配置,才能起作用。感谢园友1非烟的指正

从零开始,搭建博客系统MVC5+EF6搭建框架(3),添加Nlog日志、缓存机制(MemoryCache、RedisCache)、创建控制器父类BaseController

上一篇:日志框架对比 NLog VS Log4net


下一篇:.Net -- NLog日志框架配置与使用