在.NET项目中使用PostSharp,使用CacheManager实现多种缓存框架的处理
在前面几篇随笔中,介绍了PostSharp的使用,以及整合MemoryCache,《在.NET项目中使用PostSharp,实现AOP面向切面编程处理》、《在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理》参数了对PostSharp的使用,并介绍了MemoryCache的缓存使用,但是缓存框架的世界里面,有很多成熟的缓存框架,如MemoryCache、Redis、Memcached、Couchbase、System.Web.Caching等,这时候我们如果有一个大内总管或者一个吸星大法的武功,把它们融合起来,那么就真的是非常完美的一件事情,这个就是我们CacheManager缓存框架了,这样的灵活性缓存框架并结合了PostSharp横切面对常规代码的简化功能,简直就是好鞍配好马、宝剑赠英雄,整合起来处理缓存真的是如虎添翼。
1、CacheManager缓存框架的回顾
关于这个缓存框架,我在随笔《.NET缓存框架CacheManager在混合式开发框架中的应用(1)-CacheManager的介绍和使用》中进行了介绍,读者可以从中了解一下CacheManager缓存框架究竟是一个什么样的东西。
CacheManager是一个以C#语言开发的开源.Net缓存框架抽象层。它不是具体的缓存实现,但它支持多种缓存提供者(如Redis、Memcached等)并提供很多高级特性。
CacheManager 主要的目的使开发者更容易处理各种复杂的缓存场景,使用CacheManager可以实现多层的缓存,让进程内缓存在分布式缓存之前,且仅需几行代码来处理。
CacheManager 不仅仅是一个接口去统一不同缓存提供者的编程模型,它使我们在一个项目里面改变缓存策略变得非常容易,同时也提供更多的特性:如缓存同步、并发更新、序列号、事件处理、性能计算等等,开发人员可以在需要的时候选择这些特性。
CacheManager缓存框架支持Winform和Web等应用开发,以及支持多种流行的缓存实现,如MemoryCache、Redis、Memcached、Couchbase、System.Web.Caching等。
纵观整个缓存框架,它的特定很明显,在支持多种缓存实现外,本身主要是以内存缓存(进程内)为主,其他分布式缓存为辅的多层缓存架构方式,以达到快速命中和处理的机制,它们内部有相关的消息处理,使得即使是分布式缓存,也能够及时实现并发同步的缓存处理。
CacheManager缓存框架在配置方面,支持代码方式的配置、XML配置,以及JSON格式的配置处理,非常方便。
CacheManager缓存框架默认对缓存数据的序列化是采用二进制方式,同时也支持多种自定义序列化的方式,如基于JOSN.NET的JSON序列化或者自定义序列化方式。
CacheManager缓存框架可以对缓存记录的增加、删除、更新等相关事件进行记录。
CacheManager缓存框架的缓存数据是强类型的,可以支持各种常规类型的处理,如Int、String、List类型等各种基础类型,以及可序列号的各种对象及列表对象。
CacheManager缓存框架支持多层的缓存实现,内部良好的机制可以高效、及时的同步好各层缓存的数据。
CacheManager缓存框架支持对各种操作的日志记录。
CacheManager缓存框架在分布式缓存实现中支持对更新的锁定和事务处理,让缓存保持更好的同步处理,内部机制实现版本冲突处理。
CacheManager缓存框架支持两种缓存过期的处理,如绝对时间的过期处理,以及固定时段的过期处理,是我们处理缓存过期更加方便。
....
很多特性基本上覆盖了缓存的常规特性,而且提供的接口基本上也是我们所经常用的Add、Put、Update、Remove等接口,使用起来也非常方便。
CacheManager的GitHub源码地址为:https://github.com/MichaCo/CacheManager,如果需要具体的Demo及说明,可以访问其官网:http://cachemanager.net/。
一般来说,对于单机版本的应用场景,基本上是无需引入这种缓存框架的,因为客户端的并发量很少,而且数据请求也是寥寥可数的,性能方便不会有任何问题。
如果对于分布式的应用系统,如我在很多随笔中介绍到我的《混合式开发框架》、《Web开发框架》,由于数据请求是并发量随着用户增长而增长的,特别对于一些互联网的应用系统,极端情况下某个时间点一下可能就会达到了整个应用并发的峰值。那么这种分布式的系统架构,引入数据缓存来降低IO的并发数,把耗时请求转换为内存的高速请求,可以极大程度的降低系统宕机的风险。
我们以基于常规的Web API层来构建应用框架为例,整个数据缓存层,应该是在Web API层之下、业务实现层之上的一个层,如下所示。

2、整合PostSharp和CacheManager实现多种缓存框架的处理
由于MemoryCache是在单个机器上进行缓存的处理,而且无法进行序列号,电脑宕机后就会全部丢掉缓存内容,由于这个缺点,我们对《在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理》基础上进行进一步的调整,整合CacheManager进行使,从而可以利用缓存弹性化处理以及可序列号的特点。
我们在正常情况下,还是需要使用Redis这个强大的分布式缓存的,关于Redis的安装和使用,请参考我的随笔《基于C#的MongoDB数据库开发应用(4)--Redis的安装及使用》。
我们首先定义一个CacheAttribute的Aspect类,用来对缓存的切面处理。
/// <summary>
/// 方法实现缓存的标识
/// </summary>
[Serializable]
public class CacheAttribute : MethodInterceptionAspect
{
/// <summary>
/// 缓存的失效时间设置,默认采用30分钟
/// </summary>
public int ExpirationPeriod = ; /// <summary>
/// PostSharp的调用处理,实现数据的缓存处理
/// </summary>
public override void OnInvoke(MethodInterceptionArgs args)
{
//默认30分钟失效,如果设置过期时间,那么采用设置值
TimeSpan timeSpan = new TimeSpan(, , ExpirationPeriod, ); var cache = MethodResultCache.GetCache(args.Method, timeSpan);
var arguments = args.Arguments.ToList();//args.Arguments.Union(new[] {WindowsIdentity.GetCurrent().Name}).ToList();
var result = cache.GetCachedResult(arguments);
if (result != null)
{
args.ReturnValue = result;
return;
}
else
{
base.OnInvoke(args);
//调用后更新缓存
cache.CacheCallResult(args.ReturnValue, arguments);
}
}
}
然后就是进一步处理完善类 MethodResultCache来对缓存数据进行处理了。该类负责构造一个CacheManager管理类来对缓存进行处理,如下代码所示。

初始化缓存管理器的代码如下所示,这里利用了MemoryCache作为快速的内存缓存(主缓存),以及Redis作为序列化存储的缓存容器(从缓存),它们有内在机制进行同步处理。
/// <summary>
/// 初始化缓存管理器
/// </summary>
private void InitCacheManager()
{
_cache = CacheFactory.Build("getStartedCache", settings =>
{
settings
.WithSystemRuntimeCacheHandle("handleName")
.And
.WithRedisConfiguration("redis", config =>
{
config.WithAllowAdmin()
.WithDatabase()
.WithEndpoint("localhost", );
})
.WithMaxRetries()
.WithRetryTimeout()
.WithRedisBackplane("redis")
.WithRedisCacheHandle("redis", true)
;
});
}
对缓存结果进行处理的函数如下所示。
/// <summary>
/// 缓存结果内容
/// </summary>
/// <param name="result">待加入缓存的结果</param>
/// <param name="arguments">方法的参数集合</param>
public void CacheCallResult(object result, IEnumerable<object> arguments)
{
var key = GetCacheKey(arguments);
_cache.Remove(key); var item = new CacheItem<object>(key, result, ExpirationMode.Sliding, _expirationPeriod);
_cache.Add(item);
}
首先就是获取方法参数的键,然后移除对应的缓存,加入新的缓存,并设定缓存的失效时间段即可。
清空缓存的时候,直接调用管理类的Clear方法即可达到目的。
/// <summary>
/// 清空方法的缓存
/// </summary>
public void ClearCachedResults()
{
_cache.Clear();
}
这样,我们处理好后,在一个业务调用类里面进行设置缓存标志即可,如下代码所示。
/// <summary>
/// 获取用户全部简单对象信息,并放到缓存里面
/// </summary>
/// <returns></returns>
[Cache(ExpirationPeriod = 1)]
public static List<SimpleUserInfo> GetSimpleUsers(int userid)
{
Thread.Sleep();
//return CallerFactory<IUserService>.Instance.GetSimpleUsers(); //模拟从数据库获取数据
List<SimpleUserInfo> list = new List<SimpleUserInfo>();
for (int i = ; i < ; i++)
{
var info = new SimpleUserInfo();
info.ID = i;
info.Name = string.Concat("Name:", i);
info.FullName = string.Concat("姓名:", i);
list.Add(info);
}
return list;
}
为了测试缓存的处理,以及对Redis的支持情况,我编写了一个简单的案例,功能如下所示。

测试代码如下所示。
//测试缓存
private void button1_Click(object sender, EventArgs e)
{
Console.WriteLine(" 测试缓存: "); //测试反复调用获取数值的耗时
DateTime start = DateTime.Now;
var list = CacheService.GetSimpleUsers();
int end = (int)DateTime.Now.Subtract(start).TotalMilliseconds; Console.WriteLine(" first: " + end);
Console.WriteLine(" List: " + list.Count); //Second test
//检查不同的方法参数,对缓存值的影响
start = DateTime.Now;
list = CacheService.GetSimpleUsers();
end = (int)DateTime.Now.Subtract(start).TotalMilliseconds;
Console.WriteLine(" Second: " + end);
Console.WriteLine(" List2: " + list.Count);
} //更新缓存
private void button2_Click(object sender, EventArgs e)
{
Console.WriteLine(" 更新缓存: "); //首先获取对应键的缓存值
//然后对缓存进行修改
//最后重新加入缓存
var key = "CacheManagerAndPostSharp.CacheService.GetSimpleUsers";
var item = MethodResultCache.GetCache(key);
var argument = new List<object>(){};
var result = item.GetCachedResult(argument);
Console.WriteLine("OldResult:" + result.ToJson()); List<SimpleUserInfo> newList = result as List<SimpleUserInfo>;
if(newList != null)
{
newList.Add(new SimpleUserInfo() { ID = new Random().Next(), Name = RandomChinese.GetRandomChars() });
}
item.CacheCallResult(newList, argument);
} //清空缓存
private void button3_Click(object sender, EventArgs e)
{
Console.WriteLine(" 清空缓存: "); //首先获取对应键的缓存值
var key = "CacheManagerAndPostSharp.CacheService.GetSimpleUsers";
var item = MethodResultCache.GetCache(key);
var argument = new List<object>(){}; //然后清空方法的所有缓存
item.ClearCachedResults(); //最后重新检验缓存值为空
var result = item.GetCachedResult(argument);
Console.WriteLine("Result:" + result !=null ? result.ToJson() : "null");
}
测试运行结果如下所示。
测试缓存:
first:
List:
Second:
List2: 更新缓存:
OldResult:[
{"ID":,"HandNo":null,"Name":"Name:0","Password":null,"FullName":"姓名:0","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:1","Password":null,"FullName":"姓名:1","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:2","Password":null,"FullName":"姓名:2","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:3","Password":null,"FullName":"姓名:3","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:4","Password":null,"FullName":"姓名:4","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:5","Password":null,"FullName":"姓名:5","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:6","Password":null,"FullName":"姓名:6","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:7","Password":null,"FullName":"姓名:7","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:8","Password":null,"FullName":"姓名:8","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null},
{"ID":,"HandNo":null,"Name":"Name:9","Password":null,"FullName":"姓名:9","MobilePhone":null,"Email":null,"CurrentLoginUserId":null,"Data1":null,"Data2":null,"Data3":null}] 测试缓存:
first:
List:
Second:
List2: 清空缓存:
null
同时我们看到在Redis里面,有相关的记录如下所示。

结合PostSharp和CacheManager,使得我们在使用缓存方面更具有弹性化,可以根据情况通过配置实现使用不同的缓存处理,但是在代码中使用缓存就是只需要声明一下即可,非常方便简洁了。
在.NET项目中使用PostSharp,使用CacheManager实现多种缓存框架的处理的更多相关文章
- 在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理(转)
在之前一篇随笔<在.NET项目中使用PostSharp,实现AOP面向切面编程处理>介绍了PostSharp框架的使用,试用PostSharp能给我带来很多便利和优势,减少代码冗余,提高可 ...
- 在.NET项目中使用PostSharp,使用MemoryCache实现缓存的处理
在之前一篇随笔<在.NET项目中使用PostSharp,实现AOP面向切面编程处理>介绍了PostSharp框架的使用,试用PostSharp能给我带来很多便利和优势,减少代码冗余,提高可 ...
- 在.NET项目中使用PostSharp,实现AOP面向切面编程处理
PostSharp是一种Aspect Oriented Programming 面向切面(或面向方面)的组件框架,适用在.NET开发中,本篇主要介绍Postsharp在.NET开发中的相关知识,以及一 ...
- .NET项目中使用PostSharp
PostSharp是一种Aspect Oriented Programming 面向切面(或面向方面)的组件框架,适用在.NET开发中,本篇主要介绍Postsharp在.NET开发中的相关知识,以及一 ...
- C#开发微信门户及应用(48) - 在微信框架中整合CacheManager 缓存框架
在我们的很多框架或者项目应用中,缓存在一定程度上可以提高程序的响应速度,以及减轻服务器的承载压力,因此在一些地方我们都考虑引入缓存模块,这篇随笔介绍使用开源缓存框架CacheManager来实现数据的 ...
- IOS-在ARC项目中使用非ARC框架或者类库
1.在ARC项目中使用非ARC框架或者类库 IOS 4引入了Automatic Reference Count(ARC),编译器可以在编译时对obj-c对象进行内存管理. 之前,obj-c的内存管理方 ...
- WPF Step By Step 系列-Prism框架在项目中使用
WPF Step By Step 系列-Prism框架在项目中使用 回顾 上一篇,我们介绍了关于控件模板的用法,本节我们将继续说明WPF更加实用的内容,在大型的项目中如何使用Prism框架,并给予Pr ...
- 团队项目中js冲突
在我们平时的项目中,特别是模块化开发中.经常会遇到一些js冲突问题,特别是当项目复杂到一定程度的时候.比如项目中引入了相当多的类库,框架以后. 那么.我们如何去避免或者解决这类问题呢? 1.避免使用全 ...
- <转载> VUE项目中CSS管理
vue的scoped 在vue项目中,当 .vue文件中 <style> 标签有 *scoped 属性时,它的 CSS 只作用于当前组件中的元素,很好的实现了样式私有化的目的. 使用sco ...
随机推荐
- Activity的生命周期与加载模式——Activity的生命周期演示
当Activity处于Android应用中运行时,它的活动状态由Android以Activity栈的形式管理.当前活动的Activity位于栈顶.随着不同应用的运行,每个Activity都有可能从活动 ...
- 用mfix模拟流化床时压力边界条件和迭代步长需要注意的问题
没想到今天模拟一个冷态流化床都出现这么多问题.需要通入三种气体组成的混合物,这时入口边界的压力BC_P_g不能为零,否则会报错,但是,需要注意的是,收敛效果对这个压力边界非常敏感,我随意给了个30,结 ...
- win7下安装sdks
原文及更多内容:http://yysource.sourceforge.net/?p=103 下载和安装 Windows 调试工具 http://msdn.microsoft.com/zh-CN/wi ...
- JSP userBean setProperty getProperty指令使用
JSP userBean setProperty getProperty指令使用 javaBean的属性取决于get/set方法,而不是真实的属性名称. jsp文件: <%@ page lang ...
- eclipse中的Java项目导出成为一个可以直接双击运行的jar文件
1. 选择要到处JAR文件的工程,右键选择“Export” 2. 选择“Java-->Runnable JAR file”,点击“Next”: 3. 在“Launch configuration ...
- ASP.NET Core MVC压缩样式、脚本及总是复制文件到输出目录
前言 在.NET Core之前对于压缩样式文件和脚本我们可能需要借助第三方工具来进行压缩,但在ASP.NET MVC Core中则无需借助第三方工具来完成,本节我们来看看ASP.NET Core MV ...
- 基于MAC OS 操作系统安装、配置mysql
$ sudo mv mysql-5.1.45-osx10.6-x86_64 /usr/local/mysql$ cd /usr/local$ sudo chown -R mysql:mysql mys ...
- 编写JQuery插件-1
看到这篇文章的人相信大家都学会了jq,或者正在用jq,在这里简单介绍一下jq的插件封装: jQuery的插件主要分为3种类型: 1.封装对象方法的插件 这种插件是将对象的方法封装起来,用于对通过选择器 ...
- 【noip 2009】 乌龟棋 记忆化搜索&动规
题目背景 小明过生日的时候,爸爸送给他一副乌龟棋当作礼物. 题目描述 乌龟棋的棋盘是一行N个格子,每个格子上一个分数(非负整数).棋盘第1格是唯一的起点,第N格是终点,游戏要求玩家控制一个乌龟棋子从起 ...
- wap网页、微信内嵌网页在手机端页面窗口尺寸如何不缩放
如何让wap网页.微信内嵌网页内容(尺寸和文字)在手机端页面窗口尺寸不被强制缩放 在head中加入: <meta http-equiv="Content-Type" cont ...