System.Runtime.Caching/MemoryCache

ICacheEntry 接口中的属性:具体设置过期时间 可以参考:微软文档ICacheEntry 接口

缓存基本使用 (一) 绝对过期

AbsoluteExpirationRelativeToNow 绝对过期

在实际的使用中肯定是要使用 Id 之类的进行拼接

 var items = await memCache.GetOrCreateAsync("Test1", async (e) => {
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(10);
logger.LogInformation("从数据库中读取数据");
return await dbCtx.Test.ToArrayAsync();
});
缓存基本使用 (二) 设置一个滑动过期

SlidingExpiration 滑动过期

注意如果这个缓存一直被访问,一直不会过期,长时间会导致数据不一致问题!

 var items = await memCache.GetOrCreateAsync("Test2", async (e) => {
e.SlidingExpiration = TimeSpan.FromSeconds(10);
logger.LogInformation("Demo2从数据库中读取数据");
return await dbCtx.Test.ToArrayAsync();
});
缓存基本使用 (三)混合使用过期策略

比如这个接口访问比较频繁,我们可以使用滑动过期和绝对过期结合使用

注意:绝对过期时间一定要大于滑动过期时间

var items = await memCache.GetOrCreateAsync("Test3", async (e) => {
e.SlidingExpiration = TimeSpan.FromSeconds(10);
e.AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30);
logger.LogInformation("Demo3从数据库中读取数据");
return await dbCtx.Test.ToArrayAsync();
});

在设置一个滑动过期的时候 我发现了一个问题,就是random 随机数不支持 Double 导致时间无法精确,而且如果int 类型 缓存过多 生成的 随机数可能会导致重复,造成缓存雪崩

这里我们自己扩展一个随机Double传入(minValue,maxValue)

   public static class RondowExtensions
{ public static double NextDouble(this Random random, double minValue, double maxValue)
{
if (minValue >= maxValue) throw new ArgumentOutOfRangeException(nameof(minValue), "minValue annot be bigger than maxValue");
double x = random.NextDouble();
return x * maxValue + (1 - x) * minValue;
}
}
interface IMemoryCacheHelper
    public interface IMemoryCacheHelper
{
/// <summary>
/// 从缓存中获取数据,如果缓存中没有数据,则调用valueFactory获取数据。
/// 可以用AOP+Attribute的方式来修饰到Service接口中实现缓存,更加优美,但是没有这种方式更灵活。
/// 默认最长的缓存过期时间是expireSeconds秒,当然也可以在领域事件的Handler中调用Update更新缓存,或者调用Remove删除缓存。
/// 因为IMemoryCache会把null当成合法的值,因此不会有缓存穿透的问题,但是还是建议用我这里封装的ICacheHelper,原因如下:
/// 1)可以切换别的实现类,比如可以保存到MemCached、Redis等地方。这样可以隔离变化。
/// 2)IMemoryCache的valueFactory用起来麻烦,还要单独声明一个ICacheEntry参数,大部分时间用不到这个参数。
/// 3)这里把expireSeconds加上了一个随机偏差,这样可以避免短时间内同样的请求集中过期导致“缓存雪崩”的问题
/// 4)这里加入了缓存数据的类型不能是IEnumerable、IQueryable等类型的限制
/// </summary>
/// <typeparam name="TResult">缓存的值的类型</typeparam>
/// <param name="cacheKey">缓存的key</param>
/// <param name="valueFactory">提供数据的委托</param>
/// <param name="expireSeconds">缓存过期秒数的最大值,实际缓存时间是在[expireSeconds,expireSeconds*2)之间,这样可以一定程度上避免大批key集中过期导致的“缓存雪崩”
/// 的问题</param>
/// <returns></returns>
TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int expireSeconds = 60); Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int expireSeconds = 60); /// <summary>
/// 删除缓存的值
/// </summary>
/// <param name="cacheKey"></param>
void Remove(string cacheKey);
}
MemoryCacheHelper
    /// <summary>
/// 用ASP.NET的IMemoryCache实现的内存缓存
/// </summary>
public class MemoryCacheHelper : IMemoryCacheHelper
{
private readonly IMemoryCache memoryCache;
public MemoryCacheHelper(IMemoryCache memoryCache)
{
this.memoryCache = memoryCache;
} private static void ValidateValueType<TResult>()
{
//因为IEnumerable、IQueryable等有延迟执行的问题,造成麻烦,因此禁止用这些类型
Type typeResult = typeof(TResult);
if (typeResult.IsGenericType)//如果是IEnumerable<String>这样的泛型类型,则把String这样的具体类型信息去掉,再比较
{
typeResult = typeResult.GetGenericTypeDefinition();
}
//注意用相等比较,不要用IsAssignableTo
if (typeResult == typeof(IEnumerable<>) || typeResult == typeof(IEnumerable)
|| typeResult == typeof(IAsyncEnumerable<TResult>)
|| typeResult == typeof(IQueryable<TResult>) || typeResult == typeof(IQueryable))
{
throw new InvalidOperationException($"TResult of {typeResult} is not allowed, please use List<T> or T[] instead.");
}
} private static void InitCacheEntry(ICacheEntry entry, int baseExpireSeconds)
{
//过期时间.Random.Shared 是.NET6新增的
Random.Shared.Next(1.1, 1.0);
double sec = Random.Shared.Next(baseExpireSeconds, baseExpireSeconds * 2);
TimeSpan expiration = TimeSpan.FromSeconds(sec);
entry.AbsoluteExpirationRelativeToNow = expiration;
} public TResult? GetOrCreate<TResult>(string cacheKey, Func<ICacheEntry, TResult?> valueFactory, int baseExpireSeconds = 60)
{
ValidateValueType<TResult>();
//因为IMemoryCache保存的是一个CacheEntry,所以null值也认为是合法的,因此返回null不会有“缓存穿透”的问题
//不调用系统内置的CacheExtensions.GetOrCreate,而是直接用GetOrCreate的代码,这样免得包装一次委托
if (!memoryCache.TryGetValue(cacheKey, out TResult result))
{
using ICacheEntry entry = memoryCache.CreateEntry(cacheKey);
InitCacheEntry(entry, baseExpireSeconds);
result = valueFactory(entry)!;
entry.Value = result;
}
return result;
} public async Task<TResult?> GetOrCreateAsync<TResult>(string cacheKey, Func<ICacheEntry, Task<TResult?>> valueFactory, int baseExpireSeconds = 60)
{
ValidateValueType<TResult>();
if (!memoryCache.TryGetValue(cacheKey, out TResult result))
{
using ICacheEntry entry = memoryCache.CreateEntry(cacheKey);
InitCacheEntry(entry, baseExpireSeconds);
result = (await valueFactory(entry))!;
entry.Value = result;
}
return result;
} public void Remove(string cacheKey)
{
memoryCache.Remove(cacheKey);
}
}

ASP.NET Core :缓存系列(四):内存缓存 MemoryCache的更多相关文章

  1. ASP.NET Core Authentication系列(四)基于Cookie实现多应用间单点登录(SSO)

    前言 本系列前三篇文章分别从ASP.NET Core认证的三个重要概念,到如何实现最简单的登录.注销和认证,再到如何配置Cookie 选项,来介绍如何使用ASP.NET Core认证.感兴趣的可以了解 ...

  2. ASP.NET Core 中间件之压缩、缓存

    前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...

  3. ASP.NET Core 使用 Redis 实现分布式缓存:Docker、IDistributedCache、StackExchangeRedis

    ASP.NET Core 使用 Redis 实现分布式缓存:Docker.IDistributedCache.StackExchangeRedis 前提:一台 Linux 服务器.已安装 Docker ...

  4. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之三 —— 配置

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  5. 《ASP.NET Core 高性能系列》致敬伟大的.NET斗士甲骨文!

    写在开始 三年前,曾写过一篇文章:从.NET和Java之争谈IT这个行业,当时遭到某些自认为懂得java就了不起的Javaer抨击, 现在可以致敬伟大的.NET斗士甲骨文了 (JDK8以上都需要收费, ...

  6. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  7. 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— 准备

    ==== 目录 ==== 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之一 —— 开篇 跟我学: 使用 fireasy 搭建 asp.net core 项目系列之二 —— ...

  8. 《闲聊 ASP.NET Core》系列直播清单

    [闲聊 ASP.NET Core]第一期:项目与应用结构 [闲聊 ASP.NET Core]第二期:Web Host 初始化与生命周期事件 [闲聊ASP.NET Core]第三期:应用程序配置 [闲聊 ...

  9. [铁人赛] ASP.NET Core 2 系列- 从头开始

    来势汹汹的.NET Core似乎要取代.NET Framework,ASP.NET也随之发布.NET Core版本.虽说名称沿用ASP.NET,但相较于ASP.NET确有许多架构上的差异,可说是除了名 ...

  10. ASP.NET CORE CACHE的使用(含MemoryCache,Redis)

    原文:ASP.NET CORE CACHE的使用(含MemoryCache,Redis) 版权声明:本文为博主原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明. 本文链接 ...

随机推荐

  1. 汽车锂电池行业为啥会选择钡铼BL200系列Profinet分布式IO

    近年来,全球新能源汽车的蓬勃发展促进了锂电池行业的发展.随着锂电池标准化程度的提高,电池和模块规格的标准化是未来的发展趋势,也促进了自动化模块生产线的发展. 锂电池模块生产线通过涂胶-电池堆叠-组装- ...

  2. Iconfont(矢量图标)+iconmoon(图标svg互转)配合javascript来打造属于自己的个性化社交分享系统

    原文转载自「刘悦的技术博客」https://v3u.cn/a_id_143 每一个应用程序,其实都会有分享的需求,比如一键分享一篇文章或者一些活动到微博或者微信亦或者是twitter等社交平台,因为人 ...

  3. 妙用 CSS 构建花式透视背景效果

    本文将介绍一种巧用 background 配合 backdrop- filter 来构建有趣的透视背景效果的方式. 本技巧源自于一名群友的提问,如何构建如 ElementUI 文档的一种顶栏背景特效, ...

  4. React报错之`value` prop on `input` should not be null

    正文从这开始~ 总览 当我们把一个input的初始值设置为null或者覆盖初始值设置为null时,会产生"valueprop on input should not be null" ...

  5. GDB技巧:使用终端界面模式

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 简 ...

  6. Luogu P5030 长脖子鹿放置(网络流)

    匈牙利T了,Dinic飞了... 按奇偶连 #include <cstdio> #include <iostream> #include <cstring> #in ...

  7. HCIA-Datacom 3.1 实验一:以太网基础与VLAN配置实验

    实验介绍: 以太网是一种基于CSMA/CD(Carrier Sense Multiple Access/Collision Detection)的共享通讯介质的数据网络通讯技术.当主机数目较多时会导致 ...

  8. Nodemon 如何实时监听 TypeScript 项目下的文件并热部署?

    首先你的项目要安装ts-node和nodemon: npm i -D ts-node nodemon 在package.json文件中配置运行脚本: "dev": "no ...

  9. Silk语言-中国人自己的开源编程语言

    什么是Silk Silk语言是一门完全独立自主开发的跨平台动态类型编程语言,绝非"木兰"等套壳语言. Silk简单易学,30分钟即可掌握全部语法,让你像Python一样简单地写C/ ...

  10. Mybatis的ResultMap与limit分页查询

    ResultMap主要解决的是:属性名和字段不一致 如果在pojo中设置的是一个名字,在数据库上又是另一个名字,那么查询出来的结果或者其他操作的结果就为null. //在pojo中 private S ...