第十一节:Asp.Net Core 之内存缓存(IMemoryCache)
1. 整体说明
ASP.NET Core 支持多种不同的缓存,最简单的缓存基于 IMemoryCache,它表示存储在 Web 服务器内存中的缓存,内存缓存可以存储任何对象,存储形式键值对,需要 .net standard 2.0 或者 .Net framework 4.5 或更高版本。
本节主要介绍:依赖注入的方式使用、全局封装单例配置、缓存几个方法和性质。
2. 常规使用步骤
(1) 安装程序集:System.Runtime.Caching 和 Microsoft.Extensions.Caching.Memory,如果是是Core MVC程序自带的Microsoft.AspNetCore.App包里已经涵盖了
Microsoft.Extensions.Caching.Memory,无需重复下载。
(2) 在ConfigureService中注册内存缓存服务: services.AddMemoryCache();
1 using Microsoft.AspNetCore.Builder; 2 using Microsoft.AspNetCore.Mvc; 3 using Microsoft.Extensions.DependencyInjection; 4 5 public class Startup 6 { 7 public void ConfigureServices(IServiceCollection services) 8 { 9 services.AddMemoryCache(); 10 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 11 } 12 13 14 }
(3) 通过属性注入的方式在控制器中注入IMemoryCache对象。
1 public class FirstController : Controller 2 { 3 private IMemoryCache _cache1; 4 public FirstController(IMemoryCache memoryCache) 5 { 6 _cache1 = memoryCache; 7 } 8 }
(4) 通过get、set方法实现常规缓存的读取和写入,如下 案例①。
1 { 2 string nowTime1 = _cache1.Get<string>("t1"); 3 if (String.IsNullOrEmpty(nowTime1)) 4 { 5 nowTime1 = DateTime.Now.ToString(); 6 _cache1.Set("t1", nowTime1); 7 } 8 ViewBag.t1 = nowTime1; 9 }
3. 全局封装单例的形式
(1)新建一个memoryCacheHelp类,声明一个IMemoryCache属性,并在构造函数中进行初始化,实例化的时候可以通过SizeLimit设置全局缓存的最大Size。
特别注意:如果在这全局设置了最大Size,凡是使用的时候都需要通过SetSize进行设置,且必须 "小于等于" 这个最大Size,不设置会报错,设置的比这个大,缓存会失效。
1 public class memoryCacheHelp 2 { 3 public IMemoryCache _cache { get; set; } 4 public memoryCacheHelp() 5 { 6 _cache = new MemoryCache(new MemoryCacheOptions { 7 SizeLimit = 1024 8 }); 9 } 10 }
(2)在ConfigureService将该类注册成单例的:services.AddSingleton<memoryCacheHelp>();
1 public void ConfigureServices(IServiceCollection services) 2 { 3 services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); 4 services.AddSingleton<memoryCacheHelp>(); 5 }
(3)通过属性注入的方式在控制器中注入IMemoryCache对象
1 public class FirstController : Controller 2 { 3 private IMemoryCache _cache2; 4 public FirstController(memoryCacheHelp cache) 5 { 6 _cache2 = cache._cache; 7 } 8 }
(4)通过TryGetValue、set方法实现缓存的读取和写入,如下 案例②
注: 这里通过依赖注入的形式将memoryCacheHelp注册成单例类,和直接声明成单例类道理是一样的。
1 { 2 string nowTime2 = null; 3 if (!_cache2.TryGetValue("t2", out nowTime2)) 4 { 5 nowTime2 = DateTime.Now.ToString(); 6 //设置缓存的大小 7 var cacheOptions = new MemoryCacheEntryOptions() 8 .SetSize(100) 9 .SetAbsoluteExpiration(TimeSpan.FromSeconds(5)); 10 _cache2.Set("t2", nowTime2, cacheOptions); 11 } 12 ViewBag.t2 = nowTime2; 13 }
4. 方法、属性详解
(1) 获取数据
A. Get<T>方法:根据键名获取指定类型的值,将返回值做判断,比如根据是否为null,来判断有没有值。 如下面的案例①
B. TryGetValue(object key, out TItem value);根据key获取指定类型值,通过out参数进行输出,如果拿到值返回true,如果拿不到值返回false,相比Get<T>方法 如下面案例②
C. GetOrCreate(object key, Func<ICacheEntry, TItem> factory);适用场景:key有值则获取该值,没有值为它赋值(通过return直接返回)。如下面案例③
(2) 写入数据
A. Set方法:有很多重载,重点看下面两个
①:Set<TItem>(object key, TItem value); 最简单的键值模式
②:Set<TItem>(object key, TItem value, MemoryCacheEntryOptions options); 通过MemoryCacheEntryOptions设置缓存的性质,详见下面
B. GetOrCreate方法:当key对应的值为空则通过return返回来赋值,如下面的:案例③,通过第二个参数在Func委托中设置ICacheEntry属性进行缓存性质的设置。 详见下面(5)
1 { 2 string nowTime3 = _cache1.GetOrCreate("t3", entry => 3 { 4 entry.Size = 100; 5 //滑动过期时间设置为2秒 6 entry.SlidingExpiration = TimeSpan.FromSeconds(2); 7 //绝对和滑动只能设置一个 8 //entry.AbsoluteExpiration = new DateTimeOffset(DateTime.Parse("2019-07-16 16:33:10")); 9 entry.Priority = CacheItemPriority.High; 10 entry.RegisterPostEvictionCallback(MyCallback, this); 11 return DateTime.Now.ToString(); 12 }); 13 ViewBag.t3 = nowTime3; 14 }
(3) 移除数据
A. Remove方法:Remove(object key); 移除缓存
(4) MemoryCacheEntryOptions类
用来设置缓存的一些性质,可以通过方法或者属性进行设置,在Set方法中使用。
A. 缓存大小(SetSize方法和Size属性):如果全局单例设置缓存的最大值,则每个使用的地方都需要显式的设置,必须小于等于最大值,如果不设置报错,如果设置比最大值还大,缓存不生效。
B. 绝对过期时间(SetAbsoluteExpiration方法和AbsoluteExpiration属性):绝对指的是到了这个时间就过期,不管这期间有没有人访问。
绝对过期有两种设置方式:① 通过TimeSpan设置距离当前时间的间隔 ② 通过DateTimeOffset设置具体到某一时刻。 详见下面案例④
C. 滑动过期时间(SetSlidingExpiration方法和SlidingExpiration属性):相对是指以最后一次访问来计算,每访问一次重新计算过期时间。
D. 缓存级别(SetPriority方法和Priority属性):有Low、Normal、High、NeverRemove。
E. 缓存移除时回调(RegisterPostEvictionCallback方法和属性):缓存过期或者手动移除时调用,该方法有四个参数,调用的时候自动赋值,分别是: 键、值、消失原因、状态。
1 { 2 string nowTime4 = null; 3 if (!_cache2.TryGetValue("t4", out nowTime4)) 4 { 5 nowTime4 = DateTime.Now.ToString(); 6 MemoryCacheEntryOptions cacheOptions = null; 7 8 //设置缓存方式一:(通过方法) 9 { 10 cacheOptions = new MemoryCacheEntryOptions() 11 .SetSize(100) 12 //.SetSlidingExpiration(TimeSpan.FromSeconds(5)) 13 //绝对和滑动只能设置一个 14 .SetAbsoluteExpiration(TimeSpan.FromSeconds(60)) 15 .SetPriority(CacheItemPriority.Normal) 16 .RegisterPostEvictionCallback(MyCallback, this); 17 18 } 19 20 //设置缓存方式二:(通过属性) 21 //{ 22 // cacheOptions = new MemoryCacheEntryOptions(); 23 // cacheOptions.Size = 100; 24 // //cacheOptions.SlidingExpiration = TimeSpan.FromSeconds(5); 25 // //绝对和滑动只能设置一个 26 // cacheOptions.AbsoluteExpiration = new DateTimeOffset(DateTime.Parse("2019-07-16 16:33:10")); 27 // cacheOptions.Priority = CacheItemPriority.High; 28 // cacheOptions.RegisterPostEvictionCallback(MyCallback, this); 29 //} 30 _cache2.Set("t4", nowTime4, cacheOptions); 31 32 } 33 ViewBag.t4 = nowTime4; 34 } 35 36 /// <summary> 37 /// 失败回调 38 /// </summary> 39 /// <param name="key">键</param> 40 /// <param name="value">值</param> 41 /// <param name="reason">缓存消失的原因,比如移除、过期,是个枚举类型</param> 42 /// <param name="state"></param> 43 private static void MyCallback(object key, object value, EvictionReason reason, object state) 44 { 45 var message = $"Cache entry was removed : key={key},value={value}, reason={reason}, state={state}"; 46 }
(5) ICacheEntry
通过属性的形式设置:缓存大小、绝对过期、滑动过期、缓存级别、缓存移除回调,详见案例③
5.即时创建调用 VS 全局单例调用
首先需要明白,不管即时创建调用还是全局单例调用,MemoryCache都是存在服务器内存中的,这一点是毋庸置疑的。
(1) 即时创建调用:多个客户端每次访问该方法的时候,都需要现创建一个IMemoryCache对象,然后进行读取或写入。
(2) 全局单例调用:只要有一个客户端访问该方法,创建IMemoryCache对象,后续不管谁访问,都是使用的同一个对象进行读取或写入。
很傻瓜的一个问题:内存缓存是以键值对的形式进行存储,同一个键如果多次设置会覆盖,但是不同键之间设置过期时间是互相不影响的。
!
- 作 者 : Yaopengfei(姚鹏飞)
- 博客地址 : http://www.cnblogs.com/yaopengfei/
- 声 明1 : 本人才疏学浅,用郭德纲的话说“我是一个小学生”,如有错误,欢迎讨论,请勿谩骂^_^。
- 声 明2 : 原创博客请在转载时保留原文链接或在文章开头加上本人博客地址,否则保留追究法律责任的权利。