上一篇中,咱们用了反射工厂来解除BLL和UI层耦合的问题。固然那是最简单的解决方法,再复杂一点的程序可能思路相同,可是在编程细节中须要考虑的就更多了,好比今天我在重构过程当中遇到的问题。也是接下来我要解决的问题,缓存模块。为何要解决这个问题呢,因为咱们有些下载代码运行的小伙伴,发现怎么运行报错,原来是没有装redis。但是我只想看layim和signalr代码而已啊,不想装什么redis。那么基于昨天的经验,我把缓存模块一样提取出接口,而后加了一个原始的cache层。这个cache是基于System.Web.Caching.Cache来实现的。html
正如前言中所说,实现思路仍是利用反射工厂,读取用户的配置来反射动态生成对象。Cache代码结构调整以下:web
首先说明一下,因为接口内部方法目前只是根据项目须要来设计,可能不全面或者不够灵活,不过不要紧,后期能够完善。目前接口(ICache)中包含以下方法:redis
public interface ICache { /// <summary> /// 获取缓存,根据key /// </summary> /// <typeparam name="T"></typeparam> /// <param name="key"></param> /// <returns>返回获取到的值</returns> T Get<T>(string key); /// <summary> /// 增长缓存 /// </summary> /// <typeparam name="T">T</typeparam> /// <param name="key">key</param> /// <param name="value">value</param> /// <returns>添加成功返回true,不然返回false</returns> bool Set<T>(string key, T value); bool Set<T>(string key, T value, DateTimeOffset offset); /// <summary> /// 判断key是否存在 /// </summary> /// <param name="key">key</param> /// <returns>存在返回true,不然返回false</returns> bool Exists(string key); /// <summary> /// 删除缓存 /// </summary> /// <param name="key"></param> /// <returns>返回是否删除成功,true或者false</returns> bool Delete(string key); /// <summary> /// 获取哈希表中的值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="hashKey"></param> /// <param name="key"></param> /// <returns></returns> T HashGet<T>(string hashKey, string key); /// <summary> /// 设置哈希缓存值 /// </summary> /// <typeparam name="T"></typeparam> /// <param name="hashKey">hashKey</param> /// <param name="key">key</param> /// <param name="value">value</param> /// <returns>返回是否设置成功</returns> bool HashSet<T>(string hashKey, string key, T value); /// <summary> /// 删除哈希缓存中的某个key /// </summary> /// <param name="hashKey"></param> /// <param name="key"></param> /// <returns>返回是否删除成功</returns> bool HashDelete(string hashKey, string key); }
很简单的几个方法,相信复杂的项目或者需求确定要有更多的功能。这里姑且不讨论。而后咱们按照上图中新建文件夹,分别对应咱们要切换的Cache类型。好比,我新建了,Memcached,Redis,Memory三个文件夹,前两个不用说,第三个就是咱们使用框架自带的Cache。下面的代码是我参照网络上的一段文章又简单包装了一下写的。这里写这个Cache只是为了后续提到的,咱们可以自由切换而已。能用第三方的缓存仍是用第三方的。代码以下:(实现接口中的方法)编程
public class Cache : ICache { private static System.Web.Caching.Cache _cache; const int defaultTime = 24 * 60;//一天 public Cache() { //初始化 _cache = HostingEnvironment.Cache; SaveTime = defaultTime; } public static double SaveTime { get; set; } public bool Delete(string key) { var obj =_cache.Remove(key); return obj != null; } public bool Exists(string key) { return true; } public T Get<T>(string key) { if (string.IsNullOrEmpty(key)) { return default(T); } return (T)_cache.Get(key); } public bool HashDelete(string hashKey, string key) { Hashtable table = Get<Hashtable>(hashKey); if (table == null) { return true; } else { if (table.ContainsKey(key)) { table.Remove(key); } } return true; } public T HashGet<T>(string hashKey, string key) { Hashtable table = Get<Hashtable>(hashKey); if (table != null) { var value = table[key]; if (value == null) { return default(T); } return (T)value; } return default(T); } public bool HashSet<T>(string hashKey, string key, T value) { //这里就是用hashtable作哈希保存 Hashtable table = Get<Hashtable>(hashKey); if (table == null) { table = new Hashtable(); table.Add(key, value); } else { if (table.ContainsKey(key)) { table[key] = value; } else { table.Add(key, value); } } return Set(hashKey, table); } #region private void Insert(string key, object value, CacheDependency dependency, CacheItemPriority priority, CacheItemRemovedCallback callback) { _cache.Insert(key, value, dependency,System.Web.Caching.Cache.NoAbsoluteExpiration, TimeSpan.FromMinutes(SaveTime), priority, callback); } private void Insert(string key, object value, CacheDependency dependency, CacheItemRemovedCallback callback) { Insert(key, value, dependency, CacheItemPriority.Default, callback); } private void Insert(string key, object value, CacheDependency dependency) { Insert(key, value, dependency, CacheItemPriority.Default, null); } private void Insert(string key, object value) { Insert(key, value, null, CacheItemPriority.Default, null); } #endregion public bool Set<T>(string key, T value) { Insert(key, value); return true; } public bool Set<T>(string key, T value, DateTimeOffset offset) { SaveTime = offset.Offset.Minutes; Insert(key, value); SaveTime = defaultTime; return true; } }
这样的话,咱们一样用上一篇文章中写的方法来生成对用的ICache对象。设计模式
public class LayIMCacheFactory : LayIMFactory { public LayIMCacheFactory() { asemmblyPath = "LayIM.Cache.Classes.{0}.{1},LayIM.Cache"; _type = "CacheType"; } public ICache CreateCache() { return Create<ICache>(FactoryClasses.Cache); } public LayIMCache CreateLayIMCache() { ICache cache = CreateCache();
return new LayIMCache(cache); } }
能够看到,上述代码中,CreateLayIMCache方法中返回的是LayIMCache,是由于LayIMCache至关于业务层了,虽然也作了接口,可是(暂时)没有必要再区分了,由于,在构造函数中,我把ICache的实例传给LayIMCache中,而后内部调用相应的Cache方法,而后再最外部调用LayIMCache。可能把你们绕晕了。画个图更形象一些.(我也不会UML图,实在惭愧,将就看吧~)缓存
如今咱们来演示一下。怎么能看出不一样呢,因为Redis使可以将咱们存的缓存数据持久化的。而Memory的这个Cache只要程序停了,他就消失了。那么,咱们能够经过验证登陆token的方法来测试。首先Redis没必要说。咱们更改web.config中的CacheType值。网络
<!--缓存类型,Redis Memory,Memcached--> <add key="CacheType" value="Memory" />
这里要注意,不管是Memory仍是Redis或者其余,这里的配置字母必定要写正确,不然反射生成实例的时候会报错。框架
咱们运行一下,打断点调试:函数
这时候咱们在关闭程序,从新运行调试,咱们走到token验证,看一下内容,已经没有了,以下图,验证已经进入非受权条件内。测试
本篇已经接近尾声了,原来写代码写多了,头脑真的会升华。之前看设计模式中的代码压根体会不到其中的奥妙,现在专门作一下代码重构工做才能真正体验到代码设计的精妙之处。骚年还须要努力啊。今天的代码重构工做就到此结束,不想装Redis的同窗赶忙试试用这个方法切换缓存吧。不知道我在说些什么的同窗能够移步这里哦: