你的位置:首页 > ASP.net教程

[ASP.net教程]从零开始,搭建博客系统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类,这里都使用接口来实现,以便可以以后可以多个实现。

image

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

 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 }

View Code

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

 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 }

View Code

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

image

RedisCacheManager类

 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 }

View Code

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

image

SerializeHelper序列化帮助类

 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 }

View Code

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

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

 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 }

View Code

2.在nuget中添加Nlog框架

image

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

Nloglogger类

 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 }

View Code

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

image

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

 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>

View Code

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

image

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

image

4、日志测试

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

image

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

ExpFilter类:

 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 

View Code

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

image

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

image

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

image

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

image

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

image

四、创建BaseController类

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

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 }

View Code

JsonNetResult类:

 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 }

View Code

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

image