博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
缓存篇~第六回 Microsoft.Practices.EnterpriseLibrary.Caching实现基于方法签名的数据集缓存...
阅读量:7052 次
发布时间:2019-06-28

本文共 17559 字,大约阅读时间需要 58 分钟。

这一讲中主要是说EnterpriseLibrary企业级架构里的caching组件,它主要实现了项目缓存功能,它支持四种持久化方式,内存,文件,数据库和自定义,对于持久化不是今天讨论的重要,今天主要说,如何使用AOP的思想再配合Caching组件来实现可更新的,可插拔的,松耦合的,基于数据集(结果集)的缓存方案,之所以叫它方案,确实,在实现上有一定难度,我自己对于微软的NLayerApp架构里用到的Attribute注入方式也对一定修改,因为NLayerApp里的缓存数据集并不支持方法参数为对象和lambda表达式的情况,而我的这个方案已经解决了上面两种情况,可以说,完全支持!

我们来看一下Web.config对Caching的配置

缓存配置篇

 注册sections块

配置caching块

为unity块添加要进行缓存的方法

缓存实现篇

1 通过CachingAttribute特性对方法进行标识,并配置缓存方式,get,put,remove,一般在添加,修改操作之后,会对缓存进行remove操作

///     /// 表示由此特性所描述的方法,能够获得来自Microsoft.Practices.EnterpriseLibrary.Caching基础结构层所提供的缓存功能。    ///     [AttributeUsage(AttributeTargets.Method, AllowMultiple=false, Inherited=false)]    public class CachingAttribute : Attribute    {        #region Ctor        ///         /// 初始化一个新的
CachingAttribute
类型。 ///
/// 缓存方式。 public CachingAttribute(CachingMethod method) { this.Method = method; } /// /// 初始化一个新的
CachingAttribute
类型。 ///
/// 缓存方式。 /// 与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 public CachingAttribute(CachingMethod method, params string[] correspondingMethodNames) : this(method) { this.CorrespondingMethodNames = correspondingMethodNames; } #endregion #region Public Properties /// /// 获取或设置缓存方式。 /// public CachingMethod Method { get; set; } /// /// 获取或设置一个
值,该值表示当缓存方式为Put时,是否强制将值写入缓存中。 ///
public bool Force { get; set; } /// /// 获取或设置与当前缓存方式相关的方法名称。注:此参数仅在缓存方式为Remove时起作用。 /// public string[] CorrespondingMethodNames { get; set; } #endregion }

2 CachingMethod标识了缓存的方式

///     /// 表示用于Caching特性的缓存方式。    ///     public enum CachingMethod    {        ///         /// 表示需要从缓存中获取对象。如果缓存中不存在所需的对象,系统则会调用实际的方法获取对象,        /// 然后将获得的结果添加到缓存中。        ///         Get,        ///         /// 表示需要将对象存入缓存。此方式会调用实际方法以获取对象,然后将获得的结果添加到缓存中,        /// 并直接返回方法的调用结果。        ///         Put,        ///         /// 表示需要将对象从缓存中移除。        /// 

3 一个标准的缓存CRUD接口,它默认使用Microsoft.Practices.EnterpriseLibrary.Caching来实现,当前你可以进行利用这个接口来实现多态

///     /// 表示实现该接口的类型是能够为应用程序提供缓存机制的类型。    ///     public interface ICacheProvider    {        #region Methods        ///         /// 向缓存中添加一个对象。        ///         /// 缓存的键值,该值通常是使用缓存机制的方法的名称。        /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。        /// 需要缓存的对象。        void Add(string key, string valKey, object value);        ///         /// 向缓存中更新一个对象。        ///         /// 缓存的键值,该值通常是使用缓存机制的方法的名称。        /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。        /// 需要缓存的对象。        void Put(string key, string valKey, object value);        ///         /// 从缓存中读取对象。        ///         /// 缓存的键值,该值通常是使用缓存机制的方法的名称。        /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。        /// 
被缓存的对象。
object Get(string key, string valKey); /// /// 从缓存中移除对象。 /// /// 缓存的键值,该值通常是使用缓存机制的方法的名称。 void Remove(string key); /// /// 获取一个
值,该值表示拥有指定键值的缓存是否存在。 ///
/// 指定的键值。 ///
如果缓存存在,则返回true,否则返回false。
bool Exists(string key); /// /// 获取一个
值,该值表示拥有指定键值和缓存值键的缓存是否存在。 ///
/// 指定的键值。 /// 缓存值键。 ///
如果缓存存在,则返回true,否则返回false。
bool Exists(string key, string valKey); #endregion }

4 使用Microsoft.Practices.EnterpriseLibrary.Caching来实现缓存的持久化功能

///     /// 表示基于Microsoft Patterns & Practices - Enterprise Library Caching Application Block的缓存机制的实现。    ///     public class EntLibCacheProvider : ICacheProvider    {        #region Private Fields        private readonly ICacheManager _cacheManager = CacheFactory.GetCacheManager();        #endregion        #region ICacheProvider Members        ///         /// 向缓存中添加一个对象。        ///         /// 缓存的键值,该值通常是使用缓存机制的方法的名称。        /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。        /// 需要缓存的对象。        public void Add(string key, string valKey, object value)        {            Dictionary
dict = null; if (_cacheManager.Contains(key)) { dict = (Dictionary
)_cacheManager[key]; dict[valKey] = value; } else { dict = new Dictionary
(); dict.Add(valKey, value); } _cacheManager.Add(key, dict); } ///
/// 向缓存中更新一个对象。 /// ///
缓存的键值,该值通常是使用缓存机制的方法的名称。 ///
缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。 ///
需要缓存的对象。 public void Put(string key, string valKey, object value) { Add(key, valKey, value); } ///
/// 从缓存中读取对象。 /// ///
缓存的键值,该值通常是使用缓存机制的方法的名称。 ///
缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。 ///
被缓存的对象。
public object Get(string key, string valKey) { if (_cacheManager.Contains(key)) { Dictionary
dict = (Dictionary
)_cacheManager[key]; if (dict != null && dict.ContainsKey(valKey)) return dict[valKey]; else return null; } return null; } ///
/// 从缓存中移除对象。 /// ///
缓存的键值,该值通常是使用缓存机制的方法的名称。 public void Remove(string key) { _cacheManager.Remove(key); } ///
/// 获取一个
值,该值表示拥有指定键值的缓存是否存在。 ///
///
指定的键值。 ///
如果缓存存在,则返回true,否则返回false。
public bool Exists(string key) { return _cacheManager.Contains(key); } ///
/// 获取一个
值,该值表示拥有指定键值和缓存值键的缓存是否存在。 ///
///
指定的键值。 ///
缓存值键。 ///
如果缓存存在,则返回true,否则返回false。
public bool Exists(string key, string valKey) { return _cacheManager.Contains(key) && ((Dictionary
)_cacheManager[key]).ContainsKey(valKey); } #endregion }

5 一个工厂模块,来对缓存的持久化方式进行创建,这个一般可以在配置文件中动态去配置的,本类使用简单的单例模式来进行创建,不考虑多线程情况

///     /// 缓存持久化工厂类    ///     public sealed class CacheManager : ICacheProvider    {        #region Private Fields        private readonly ICacheProvider _cacheProvider;        private static readonly CacheManager _instance = new CacheManager();        #endregion        #region Ctor        static CacheManager() { }        private CacheManager()        {            _cacheProvider = new EntLibCacheProvider();        }        #endregion        #region Public Properties        ///         /// 获取
CacheManager
类型的单件(Singleton)实例。 ///
public static CacheManager Instance { get { return _instance; } } #endregion #region ICacheProvider Members /// /// 向缓存中添加一个对象。 /// /// 缓存的键值,该值通常是使用缓存机制的方法的名称。 /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。 /// 需要缓存的对象。 public void Add(string key, string valKey, object value) { _cacheProvider.Add(key, valKey, value); } /// /// 向缓存中更新一个对象。 /// /// 缓存的键值,该值通常是使用缓存机制的方法的名称。 /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。 /// 需要缓存的对象。 public void Put(string key, string valKey, object value) { _cacheProvider.Put(key, valKey, value); } /// /// 从缓存中读取对象。 /// /// 缓存的键值,该值通常是使用缓存机制的方法的名称。 /// 缓存值的键值,该值通常是由使用缓存机制的方法的参数值所产生。 ///
被缓存的对象。
public object Get(string key, string valKey) { return _cacheProvider.Get(key, valKey); } /// /// 从缓存中移除对象。 /// /// 缓存的键值,该值通常是使用缓存机制的方法的名称。 public void Remove(string key) { _cacheProvider.Remove(key); } /// /// 获取一个
值,该值表示拥有指定键值的缓存是否存在。 ///
/// 指定的键值。 ///
如果缓存存在,则返回true,否则返回false。
public bool Exists(string key) { return _cacheProvider.Exists(key); } /// /// 获取一个
值,该值表示拥有指定键值和缓存值键的缓存是否存在。 ///
/// 指定的键值。 /// 缓存值键。 ///
如果缓存存在,则返回true,否则返回false。
public bool Exists(string key, string valKey) { return _cacheProvider.Exists(key, valKey); } #endregion }

7 最后贡献缓存拦截类,这是核心,是提供AOP功能的核心,其中自己添加了对结构体和lambda表达式和类的方法参数的支持,原版应该是陈晴阳写的,但它不支持结构体和lambda和类,所以,我的版本把它完善了。

///     /// 表示用于方法缓存功能的拦截行为。    ///     public class CachingBehavior : IInterceptionBehavior    {        #region Private Methods        ///         /// 根据指定的
以及
实例, /// 获取与某一特定参数值相关的键名。 ///
///
实例。 ///
实例。 ///
与某一特定参数值相关的键名。
private string GetValueKey(CachingAttribute cachingAttribute, IMethodInvocation input) { switch (cachingAttribute.Method) { // 如果是Remove,则不存在特定值键名,所有的以该方法名称相关的缓存都需要清除 case CachingMethod.Remove: return null; // 如果是Get或者Put,则需要产生一个针对特定参数值的键名 case CachingMethod.Get: case CachingMethod.Put: if (input.Arguments != null && input.Arguments.Count > 0) { var sb = new StringBuilder(); for (int i = 0; i < input.Arguments.Count; i++) { if (input.Arguments[i].GetType().BaseType == typeof(LambdaExpression))//lambda处理 { var exp = input.Arguments[i] as LambdaExpression; var arr = ((System.Runtime.CompilerServices.Closure)(((System.Delegate)(Expression.Lambda(exp).Compile().DynamicInvoke())).Target)).Constants; Type t = arr[0].GetType(); string result = ""; foreach (var member in t.GetFields()) { result += member.Name + "_" + t.GetField(member.Name).GetValue(arr[0]) + "_"; } result = result.Remove(result.Length - 1); sb.Append(result.ToString()); } else if (input.Arguments[i].GetType() != typeof(string)//类和结构体处理 && input.Arguments[i].GetType().BaseType.IsClass) { var obj = input.Arguments[i]; Type t = obj.GetType(); string result = ""; foreach (var member in t.GetProperties()) { result += member.Name + "_" + t.GetProperty(member.Name).GetValue(obj) + "_"; } result = result.Remove(result.Length - 1); sb.Append(result.ToString()); } else//简单值类型处理 { sb.Append(input.Arguments[i].ToString()); } if (i != input.Arguments.Count - 1) sb.Append("_"); } return sb.ToString(); } else return "NULL"; default: throw new InvalidOperationException("无效的缓存方式。"); } } #endregion #region IInterceptionBehavior Members /// /// 获取当前行为需要拦截的对象类型接口。 /// ///
所有需要拦截的对象类型接口。
public IEnumerable
GetRequiredInterfaces() { return Type.EmptyTypes; } ///
/// 通过实现此方法来拦截调用并执行所需的拦截行为。 /// ///
调用拦截目标时的输入信息。 ///
通过行为链来获取下一个拦截行为的委托。 ///
从拦截目标获得的返回信息。
public IMethodReturn Invoke(IMethodInvocation input, GetNextInterceptionBehaviorDelegate getNext) { var method = input.MethodBase; var key = method.Name; if (method.IsDefined(typeof(CachingAttribute), false)) { var cachingAttribute = (CachingAttribute)method.GetCustomAttributes(typeof(CachingAttribute), false)[0]; var valKey = GetValueKey(cachingAttribute, input); switch (cachingAttribute.Method) { case CachingMethod.Get: try { if (CacheManager.Instance.Exists(key, valKey)) { var obj = CacheManager.Instance.Get(key, valKey); var arguments = new object[input.Arguments.Count]; input.Arguments.CopyTo(arguments, 0); return new VirtualMethodReturn(input, obj, arguments); } else { var methodReturn = getNext().Invoke(input, getNext); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Put: try { var methodReturn = getNext().Invoke(input, getNext); if (CacheManager.Instance.Exists(key)) { if (cachingAttribute.Force) { CacheManager.Instance.Remove(key); CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Put(key, valKey, methodReturn.ReturnValue); } else CacheManager.Instance.Add(key, valKey, methodReturn.ReturnValue); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } case CachingMethod.Remove: try { var removeKeys = cachingAttribute.CorrespondingMethodNames; foreach (var removeKey in removeKeys) { if (CacheManager.Instance.Exists(removeKey)) CacheManager.Instance.Remove(removeKey); } var methodReturn = getNext().Invoke(input, getNext); return methodReturn; } catch (Exception ex) { return new VirtualMethodReturn(input, ex); } default: break; } } return getNext().Invoke(input, getNext); } ///
/// 获取一个
值,该值表示当前拦截行为被调用时,是否真的需要执行 /// 某些操作。 ///
public bool WillExecute { get { return true; } } #endregion }

缓存调用篇

我们的缓存只能配置在接口的方法中,这主要考虑到unity的注入环节和面向对象的多态特性,本例中,缓存这块配置在了BLL层中,当然,如果你的架构允许,也可以做在DATA层中,当然DATA层的缓存力度可能太大,我觉得并不太合适,但代码可能更精简,所

以,大家要因情况而议,到在哪层都没问题。

public interface IUserService    {        [Caching(CachingMethod.Get)]        PagedList
GetWebManageUsers(PageParameters pp); [Caching(CachingMethod.Get)] PagedList
GetWebManageUsers(Expression
> predicate, PageParameters pp); [Caching(CachingMethod.Remove, "GetWebManageUsers")] void InsertManageUsers(NLayer_IoC_Demo.Entity.WebManageUsers entity); }
private readonly IUserService _userService = ServiceLocator.Instance.GetService
(); public ActionResult Index(string name, int page = 1) { ViewBag.Message = "缓存篇"; if (string.IsNullOrWhiteSpace(name)) return View(_userService.GetWebManageUsers(new PageParameters(page, 3))); else { Expression
> predicate = i => i.LoginName.Contains(name); return View(_userService.GetWebManageUsers(predicate, new PageParameters(page, 3))); } }

缓存配置好后,可以使用sql profiler等监控工具去查看数据库的访问情况!

 

转载地址:http://vudol.baihongyu.com/

你可能感兴趣的文章
干研发更喜欢无服务器,搞DevOps偏爱容器?
查看>>
《领导力敏捷》作者访谈
查看>>
Vue2.0 学习笔记
查看>>
研究人员发现:基于文本的AI模型容易受到改述攻击
查看>>
物联网技术周报第 103 期: DIY 智能音箱:基于 Raspberry Pi + Snowboy + AVS
查看>>
Creating Great Teams作者问答
查看>>
Azure编配器简化有状态无服务器工作流的创建
查看>>
AWS App Mesh:用于Envoy的服务网格控制平面
查看>>
专访ThoughtWorks王磊:从单块架构到微服务架构
查看>>
JetBrains大力推广Kotlin为哪般?
查看>>
IBM首家发布了公有云中的裸机Kubernetes
查看>>
火掌柜iOS端基于CocoaPods的组件二进制化实践
查看>>
Zabbix Agent端配置文件说明
查看>>
2.10环境变量PATH;2.11cp命令;2.12mv命令;2.13文档查看cat_more...
查看>>
mysql使用索引优化查询效率
查看>>
Salt Syndic配置
查看>>
优秀的开源系统恢复软件
查看>>
IE浏览器低版本判断及升级提示
查看>>
乳腺增生的早期症状?乳腺增生做什么检查最好
查看>>
java B2B2C springmvc mybatis仿淘宝电子商城系统-Hystrix服务容错
查看>>