在.NET项目中接纳PostSharp,使用MemoryCache完成缓存的拍卖(转)

在前头一篇随笔《在.NET项目中央银行使PostSharp,完毕AOP面向切面编制程序处理》介绍了PostSharp框架的利用,使用PostSharp能给本身带来许多方便和优势,减少代码冗余,升高可读性,况且能够更上一层楼高雅的实现通常的日记、极度、缓存、事务等作业场景的拍卖。本篇主要介绍使用MemoryCache完成缓存的管理。

1、MemoryCache的介绍回看

上篇未有谈起缓存的管理,一般情状下,缓存的拍卖我们能够使用微软的布满式缓存组件MemoryCache举行缓存的管理操作。MemoryCache的利用网络介绍的十分的少,可是那一个是.NET4.0新引进的缓存对象,首如若替换原本集团库的缓存模块,使得.NET的缓存能够无处不在,而不用基于特定的Windows版本上接纳。

缓存在重重景况下要求采用,合理施用缓存能够一边能够增长度序的响应速度,同时能够减小对一定能源采访的压力。本文主要针对本人在Winform方面包车型客车缓存使用做叁个引导性的牵线,希望咱们能够从中了然部分缓存的施用境况和行使格局。缓存是三个中山大学型系统所不可不思索的主题材料。为了制止每一次乞求都去做客后台的财富(比方数据库),大家一般会虚拟将部分创新不是很频仍的,能够接纳的多寡,通过一定的艺术有时地保存起来,后续的呼吁依据气象能够一直访谈这一个保存起来的数量。这种体制就是所谓的缓存机制。

.NET
4.0的缓存成效首要由三片段组成:System.Runtime.Caching,System.Web.Caching.Cache和Output
Cache。

System.Runtime.Caching那是在.NET
4.0中新扩张的缓存框架,首假使接纳MemoryCache对象,该对象存在于程序集System.Runtime.Caching.dll。

System.Web.Caching.Cache这几个则是在.NET2.0开首就直接留存的缓存对象,一般主要用在Web中,当然也得以用来Winform里面,可是要援引System.Web.dll。

Output Cache则是Asp.NET里面使用的,在ASP.NET
4.0事先的版本都是直接使用System.Web.Caching.Cache来缓存HTML片段。在ASP.NET
4.0中对它举办了再一次设计,提供了七个OutputCacheProvider供开拓职员举办扩展,可是它私下认可意况下,照旧使用System.Web.Caching.Cache来做做缓存

自己在以前的一篇小说《Winform里面包车型大巴缓存使用》曾经介绍了MemoryCache协理类的管理,用来平价完毕缓存的数额操作。它的协理类首要代码如下所示。

    /// <summary>
    /// 基于MemoryCache的缓存辅助类
    /// </summary>
    public static class MemoryCacheHelper
    {
        private static readonly Object locker = new object();

        /// <summary>
        /// 创建一个缓存的键值,并指定响应的时间范围,如果失效,则自动获取对应的值
        /// </summary>
        /// <typeparam name="T">对象类型</typeparam>
        /// <param name="key">对象的键</param>
        /// <param name="cachePopulate">获取缓存值的操作</param>
        /// <param name="slidingExpiration">失效的时间范围</param>
        /// <param name="absoluteExpiration">失效的绝对时间</param>
        /// <returns></returns>
        public static T GetCacheItem<T>(String key, Func<T> cachePopulate, TimeSpan? slidingExpiration = null, DateTime? absoluteExpiration = null)
        {
            if(String.IsNullOrWhiteSpace(key)) throw new ArgumentException("Invalid cache key");
            if(cachePopulate == null) throw new ArgumentNullException("cachePopulate");
            if(slidingExpiration == null && absoluteExpiration == null) throw new ArgumentException("Either a sliding expiration or absolute must be provided");

            if(MemoryCache.Default[key] == null)
            {
                lock(locker)
                {
                    if(MemoryCache.Default[key] == null)
                    {
                        var item = new CacheItem(key, cachePopulate());
                        var policy = CreatePolicy(slidingExpiration, absoluteExpiration);

                        MemoryCache.Default.Add(item, policy);
                    }
                }
            }

            return (T)MemoryCache.Default[key];
        }

        private static CacheItemPolicy CreatePolicy(TimeSpan? slidingExpiration, DateTime? absoluteExpiration)
        {
            var policy = new CacheItemPolicy();

            if(absoluteExpiration.HasValue)
            {
                policy.AbsoluteExpiration = absoluteExpiration.Value;
            }
            else if(slidingExpiration.HasValue)
            {
                policy.SlidingExpiration = slidingExpiration.Value;
            }

            policy.Priority = CacheItemPriority.Default;

            return policy;
        }

        /// <summary>
        /// 清空缓存
        /// </summary>
        public static void ClearCache()
        {
            List<string> cacheKeys = MemoryCache.Default.Select(kvp => kvp.Key).ToList();
            foreach (string cacheKey in cacheKeys)
            {
                MemoryCache.Default.Remove(cacheKey);
            }
        }

      ...//省略部分代码

    }

而大家在程序中,若是供给利用缓存,那么调用那么些协理类来化解,也毕竟相比较有利的,达成缓存的代码如下所示。

    public static class UserCacheService
    {
        /// <summary>
        /// 获取用户全部简单对象信息,并放到缓存里面
        /// </summary>
        /// <returns></returns>
        public static List<SimpleUserInfo> GetSimpleUsers()
        {
            System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod();
            string key = string.Format("{0}-{1}", method.DeclaringType.FullName, method.Name);

            return MemoryCacheHelper.GetCacheItem<List<SimpleUserInfo>>(key,
                delegate() {
                    //return CallerFactory<IUserService>.Instance.GetSimpleUsers(); 

                    //模拟从数据库获取数据
                    List<SimpleUserInfo> list = new List<SimpleUserInfo>();
                    for(int i = 0; i< 10; i++)
                    {
                        var info = new SimpleUserInfo();
                        info.ID = i;
                        info.Name = string.Concat("Name:", i);
                        info.FullName = string.Concat("姓名:", i);
                        list.Add(info);
                    }
                    return list;
                },
                new TimeSpan(0, 10, 0));//10分钟过期
        }

        /// <summary>
        /// 根据用户的ID,获取用户的登陆名称,并放到缓存里面
        /// </summary>
        /// <param name="userId">用户的ID</param>
        /// <returns></returns>
        public static string GetNameByID(string userId)
        {
            string result = "";
            if (!string.IsNullOrEmpty(userId))
            {
                System.Reflection.MethodBase method = System.Reflection.MethodBase.GetCurrentMethod();
                string key = string.Format("{0}-{1}-{2}", method.DeclaringType.FullName, method.Name, userId);

                result = MemoryCacheHelper.GetCacheItem<string>(key,
                    delegate() {
                        //return CallerFactory<IUserService>.Instance.GetNameByID(userId.ToInt32()); 

                        return string.Concat("Name:", userId);
                    },
                    new TimeSpan(0, 30, 0));//30分钟过期
            }
            return result;
        }

地点案例笔者模拟构造数据库数据再次来到,不然一般选择BLLFactory<T>、或许夹杂框架客商端里面使用CallerFactory<T>举办调用接口了,也正是供给对它们实行更为的函数封装管理本事达到规定的标准目标。

案例中能够安装失效缓存时间,而且失效后,自动通过Func<T>
cachePopulate的函数重新得到缓存内容,在实情下,也是那四个智能的一种管理方式。

2、结合PostSharp和MemoryCache达成缓存

地点的案例使用MemoryCache协助类来兑现缓存的拍卖,能够消除实际的难点,可是与此同不常候难题也来了,每一遍缓存管理,都急需写一段额外的代码实行管理,代码的冗余就可怜多了,并且只要比比较多地点使用缓存,那么维护这几个代码就很成难题。

作者们期望引进PostSharp手艺,来压缩系统的重新代码,减少模块间的耦合度,并利于以后的可操作性和可维护性。这种AOP的代码织入本领能够很好分离横切面和专门的工作管理,进而完成简化代码的指标。

就地点的代码难点,我们来看看,引进PostSharp后,大家的代码是何等兑现缓存管理的。

    /// <summary>
    /// 使用PostSharp,结合MemoryCache实现缓存的处理类
    /// </summary>
    public class CacheService
    {               
        /// <summary>
        /// 获取用户全部简单对象信息,并放到缓存里面
        /// </summary>
        /// <returns></returns>
        [Cache(ExpirationPeriod = 30)]
        public static List<SimpleUserInfo> GetSimpleUsers(int userid)
        {//return CallerFactory<IUserService>.Instance.GetSimpleUsers(); 

            //模拟从数据库获取数据
            List<SimpleUserInfo> list = new List<SimpleUserInfo>();
            for (int i = 0; i < 10; i++)
            {
                var info = new SimpleUserInfo();
                info.ID = i;
                info.Name = string.Concat("Name:", i);
                info.FullName = string.Concat("姓名:", i);
                list.Add(info);
            }
            return list;
        }

        /// <summary>
        /// 根据用户的ID,获取用户的登陆名称,并放到缓存里面
        /// </summary>
        /// <param name="userId">用户的ID</param>
        /// <returns></returns>
        [Cache]
        public static string GetNameByID(string userId)
        {//return CallerFactory<IUserService>.Instance.GetNameByID(userId.ToInt32()); 
            return string.Concat("Name:", userId);
        }
    }

大家注意到了下边包车型地铁函数代码,除了调用业务逻辑(这里构造数据演示)外,其实是不曾剩余的别的代码的。可是大家是在函数开端开展了四个性格的标记:

[Cache(ExpirationPeriod = 30)]

或者

[Cache]

本条正是我们证明使用缓存管理的函数,如此而已,是否非常轻巧了。

咱俩来看看生成后的代码反编写翻译获得的结果,如下所示。

图片 1

那么些和大家其实的代码是不太雷同的,这里整合了PostSharp的织入代码,进而能够完成缓存的管理操作了,不过我们在开拓进度中是透明的,只要求珍惜好和煦编写的代码就能够。

那么些里面供给选取了CacheAttribute来进行标志,这些类的代码正是应用了Post夏普的基类进行拍卖了

    /// <summary>
    /// 方法实现缓存的标识
    /// </summary>
    [Serializable]
    public class CacheAttribute : MethodInterceptionAspect
    {
        /// <summary>
        /// 缓存的失效时间设置,默认采用30分钟
        /// </summary>
        public int ExpirationPeriod = 30;

        /// <summary>
        /// PostSharp的调用处理,实现数据的缓存处理
        /// </summary>
        public override void OnInvoke(MethodInterceptionArgs args)
        {
            //默认30分钟失效,如果设置过期时间,那么采用设置值
            TimeSpan timeSpan = new TimeSpan(0, 0, ExpirationPeriod, 0);

            var cache = MethodResultCache.GetCache(args.Method, timeSpan);
            var arguments = args.Arguments.ToList();
            var result = cache.GetCachedResult(arguments);
            if (result != null)
            {
                args.ReturnValue = result;
                return;
            }
            else
            {
                base.OnInvoke(args);

                //调用后重新更新缓存
                cache.CacheCallResult(args.ReturnValue, arguments);
            }
        }
    }

本条CacheAttribute本性类包括两个设置失效的岁月距离(分钟),来内定函数重回结果的失灵时间的,通过持续MethodInterceptionAspect基类,我们重写了void
OnInvoke(MethodInterceptionArgs
args)函数,进而对调用进程的横切面进行涉企:

若是调用过程中猎取缓存结果,则直接再次来到,不必要调用函数业务逻辑;不然调用函数获得重回值,并再度安装缓存结果值。

在函数代码里面,通过传播参数(富含方法对象、超时时间等)完成方式缓存对象的营造。

MethodResultCache.GetCache(args.Method, timeSpan);

在MethodResultCache里面,我们正是对艺术的缓存实行拍卖的,首先须要声惠氏(Nutrilon)个MemoryCache的对象用于管理缓存(布满式缓存)。

        /// <summary>
        /// 初始化缓存管理器
        /// </summary>
        private void InitCacheManager()
        {
            _cache = new MemoryCache(_methodName);
        }

里头通过函数获取形式和参数的键,也正是无可比拟的键。

        /// <summary>
        /// 根据调用方法名称和参数获取缓存键
        /// </summary>
        /// <param name="arguments">方法的参数列表</param>
        /// <returns></returns>
        private string GetCacheKey(IEnumerable<object> arguments)
        {
            var key = string.Format("{0}({1})", _methodName,
              string.Join(", ", arguments.Select(x => x != null ? x.ToString() : "<Null>")));
            return key;
        }

安装缓存的操作,大家正是调用MemoryCache缓存管理类来兑现的键值设置的,如下代码所示。

        /// <summary>
        /// 缓存结果内容
        /// </summary>
        /// <param name="result">待加入缓存的结果</param>
        /// <param name="arguments">方法的参数集合</param>
        public void CacheCallResult(object result, IEnumerable<object> arguments)
        {
            _cache.Set(GetCacheKey(arguments), result, DateTimeOffset.Now.Add(_expirationPeriod));
        }

像这种类型我们就设置了贰个键值的缓存,并钦点了缓存的失效时间,在这么些时间段内,大家每趟得到的数码,无需再行调用外界接口,直接从缓存里面获取,速度增加广大,同一时间也缓解了布满式构架中的服务器承载的IO压力。

大家能够编写一小段代码实行测量试验出来的功效,如下代码所示。

            //First test
            DateTime start = DateTime.Now;
            var list = CacheService.GetSimpleUsers(1);
            int end = (int)DateTime.Now.Subtract(start).TotalMilliseconds;

            Console.WriteLine(" first: " + end);

            //Second test
            start = DateTime.Now; 
            list = CacheService.GetSimpleUsers(2);
            end = (int)DateTime.Now.Subtract(start).TotalMilliseconds;
            Console.WriteLine(" Second: " + end);

获得的结果如下所示(分别介绍获得结果的光阴)。

 first: 519
 Second: 501

 first: 0
 Second: 0

 first: 0
 Second: 0

从上边代码能够看到,第二遍呼吁数据的有必然的年月差,前边央求微秒数则是直接0了。

透过上边的 PostSharp和MemoryCache的重组,大家得以小幅度简化了缓存的处理代码,况兼能够使用比较不错的MemoryCache缓存管理类来落到实处缓存的管理,特别便于和急速了。

作者有几篇有关的小说,也顺带顶一下,确实不错:

在.NET项目中央银行使PostSharp,完毕AOP面向切面编制程序处理

在.NET项目中使用PostSharp,使用MemoryCache完成缓存的管理

在.NET项目中运用PostSharp,使用CacheManager完结二种缓存框架的处理

 

相关文章