.net封装StackExchange.Redis
Prrogram.cs 增加AddRedisCacheSetup() 用于注册服务services.AddRedisCacheSetup();// 1. 注册 RedisRepositoryTransient 生命周期// 2. 注册 ConnectionMultiplexerSingleton 生命周期using Microsoft.Extensions.DependencyInjection; using TMom.Infrastructure; using StackExchange.Redis; namespace TMom.Application.Ext { /// summary /// Redis缓存 启动服务 /// /summary public static class RedisCacheSetup { public static void AddRedisCacheSetup(this IServiceCollection services) { if (services null) throw new ArgumentNullException(nameof(services)); services.AddTransientIRedisRepository, RedisRepository(); services.AddSingletonConnectionMultiplexer(sp { string redisConfiguration Appsettings.app([Redis, ConnectionString]); var configuration ConfigurationOptions.Parse(redisConfiguration, true); configuration.ResolveDns true; configuration.AbortOnConnectFail false; configuration.SyncTimeout Appsettings.app([Redis, Timeout]).ObjToInt(); return ConnectionMultiplexer.Connect(configuration); }); } } }1. 为什么需要 RedisRepository直接使用 StackExchange.Redis 的 IDatabase 存在以下问题- API 太底层 每次操作都要处理序列化、Key 前缀等细节- 代码重复 每个 Service 都要写相同的序列化逻辑- 缺乏统一管理 Key 命名不规范容易冲突RedisRepository 的作用 封装 StackExchange.Redis提供更简洁、更易用的 API。using Microsoft.Extensions.Logging; using Newtonsoft.Json; using StackExchange.Redis; namespace TMom.Infrastructure { public class RedisRepository : IRedisRepository { private readonly ILoggerRedisRepository _logger; private readonly ConnectionMultiplexer _redis; private readonly IDatabase _database; private readonly string _instanceName Appsettings.app([Redis, InstanceName]); public RedisRepository(ILoggerRedisRepository logger, ConnectionMultiplexer redis) { _logger logger; _redis redis; _database redis.GetDatabase(); } /// summary /// 统一处理key前缀 /// /summary /// param namekey/param /// returns格式: 当前项目配置的实例:key/returns private string RenderKeyPrefix(string key) { if (key.StartsWith(${_instanceName}:)) return key; return ${_instanceName}:{key}; } private IServer GetServer() { var endpoint _redis.GetEndPoints(); return _redis.GetServer(endpoint.First()); } /// summary /// redisDb /// /summary public IDatabase Db _database; /// summary /// pub/sub /// /summary /// returns/returns public ISubscriber GetSubscriber() _redis.GetSubscriber(); public async Task Clear() { foreach (var endPoint in _redis.GetEndPoints()) { var server GetServer(); foreach (var key in server.Keys()) { await _database.KeyDeleteAsync(key); } } } public async Taskbool Exist(string key) { key RenderKeyPrefix(key); return await _database.KeyExistsAsync(key); } public async Taskstring GetValue(string key) { key RenderKeyPrefix(key); return await _database.StringGetAsync(key); } public async Task Remove(string key) { key RenderKeyPrefix(key); await _database.KeyDeleteAsync(key); } /// summary /// 重命名key /// /summary /// param namekey/param /// param namenewKey/param /// returns/returns public async Task KeyRename(string key, string newKey) { key RenderKeyPrefix(key); newKey RenderKeyPrefix(newKey); await _database.KeyRenameAsync(key, newKey); } public async Task Set(string key, object value, TimeSpan cacheTime) { if (value ! null) { key RenderKeyPrefix(key); if (value is string cacheValue) { // 字符串无需序列化 await _database.StringSetAsync(key, cacheValue, cacheTime); } else { //序列化将object值生成RedisValue await _database.StringSetAsync(key, SerializeHelper.Serialize(value), cacheTime); } } } public async TaskTEntity GetTEntity(string key) { key RenderKeyPrefix(key); var value await _database.StringGetAsync(key); if (value.HasValue) { //需要用的反序列化将Redis存储的Byte[]进行反序列化 return SerializeHelper.DeserializeTEntity(value); } else { return default(TEntity); } } public async TaskTEntity HashGetTEntity(string key, string field) { key RenderKeyPrefix(key); var value await _database.HashGetAsync(key, field); if (value.HasValue) { return SerializeHelper.DeserializeTEntity(value); } else { return default(TEntity); } } public async Task HashSetTEntity(string key, string field, TEntity value, TimeSpan? span null, When when When.Always, CommandFlags flags CommandFlags.None) { key RenderKeyPrefix(key); await _database.HashSetAsync(key, field, SerializeHelper.Serialize(value), when, flags); if (span null) return; string script redis.call(EXPIRE, key, expire); var prepared LuaScript.Prepare(script); await _database.ScriptEvaluateAsync(prepared, new { key, expire span.Value.TotalSeconds }, flags); } public async Task HashDelete(string key, string field) { key RenderKeyPrefix(key); await _database.HashDeleteAsync(key, field); } public async Taskbool HashExist(string key, string field) { key RenderKeyPrefix(key); return await _database.HashExistsAsync(key, field); } /// summary /// 根据key模糊匹配 /// /summary /// param namepattern/param /// returns/returns public async TaskRedisResult GetByPatternAsync(string pattern) { pattern RenderKeyPrefix(pattern); return await _database.ScriptEvaluateAsync(LuaScript.Prepare( local res redis.call(KEYS, keypattern) return res ), new { keypattern pattern }); } /// summary /// 根据key获取RedisValue /// /summary /// typeparam nameT/typeparam /// param nameredisKey/param /// returns/returns public async TaskRedisValue[] ListRangeAsync(string redisKey) { redisKey RenderKeyPrefix(redisKey); return await _database.ListRangeAsync(redisKey); } /// summary /// 在列表头部插入值。如果键不存在先创建再插入值 /// /summary /// param nameredisKey/param /// param nameredisValue/param /// returns/returns public async Tasklong ListLeftPushAsync(string redisKey, string redisValue, int db -1) { redisKey RenderKeyPrefix(redisKey); return await _database.ListLeftPushAsync(redisKey, redisValue); } /// summary /// 在列表尾部插入值。如果键不存在先创建再插入值 /// /summary /// param nameredisKey/param /// param nameredisValue/param /// returns/returns public async Tasklong ListRightPushAsync(string redisKey, string redisValue, int db -1) { redisKey RenderKeyPrefix(redisKey); return await _database.ListRightPushAsync(redisKey, redisValue); } /// summary /// 在列表尾部插入数组集合。如果键不存在先创建再插入值 /// /summary /// param nameredisKey/param /// param nameredisValue/param /// returns/returns public async Tasklong ListRightPushAsync(string redisKey, IEnumerablestring redisValue, int db -1) { redisKey RenderKeyPrefix(redisKey); var redislist new ListRedisValue(); foreach (var item in redisValue) { redislist.Add(item); } return await _database.ListRightPushAsync(redisKey, redislist.ToArray()); } /// summary /// 移除并返回存储在该键列表的第一个元素 反序列化 /// /summary /// param nameredisKey/param /// returns/returns public async TaskT ListLeftPopAsyncT(string redisKey, int db -1) where T : class { redisKey RenderKeyPrefix(redisKey); return JsonConvert.DeserializeObjectT(await _database.ListLeftPopAsync(redisKey)); } /// summary /// 移除并返回存储在该键列表的最后一个元素 反序列化 /// 只能是对象集合 /// /summary /// param nameredisKey/param /// returns/returns public async TaskT ListRightPopAsyncT(string redisKey, int db -1) where T : class { redisKey RenderKeyPrefix(redisKey); return JsonConvert.DeserializeObjectT(await _database.ListRightPopAsync(redisKey)); } /// summary /// 移除并返回存储在该键列表的第一个元素 /// /summary /// param nameredisKey/param /// param namedb/param /// returns/returns public async Taskstring ListLeftPopAsync(string redisKey, int db -1) { redisKey RenderKeyPrefix(redisKey); return await _database.ListLeftPopAsync(redisKey); } /// summary /// 移除并返回存储在该键列表的最后一个元素 /// /summary /// typeparam nameT/typeparam /// param nameredisKey/param /// param namedb/param /// returns/returns public async Taskstring ListRightPopAsync(string redisKey, int db -1) { redisKey RenderKeyPrefix(redisKey); return await _database.ListRightPopAsync(redisKey); } /// summary /// 列表长度 /// /summary /// param nameredisKey/param /// param namedb/param /// returns/returns public async Tasklong ListLengthAsync(string redisKey, int db -1) { redisKey RenderKeyPrefix(redisKey); return await _database.ListLengthAsync(redisKey); } /// summary /// 返回在该列表上键所对应的元素 /// /summary /// param nameredisKey/param /// returns/returns public async TaskIEnumerablestring ListRangeAsync(string redisKey, int db -1) { redisKey RenderKeyPrefix(redisKey); var result await _database.ListRangeAsync(redisKey); return result.Select(o o.ToString()); } /// summary /// 根据索引获取指定位置数据 /// /summary /// param nameredisKey/param /// param namestart/param /// param namestop/param /// param namedb/param /// returns/returns public async TaskIEnumerablestring ListRangeAsync(string redisKey, int start, int stop, int db -1) { redisKey RenderKeyPrefix(redisKey); var result await _database.ListRangeAsync(redisKey, start, stop); return result.Select(o o.ToString()); } /// summary /// 删除List中的元素 并返回删除的个数 /// /summary /// param nameredisKeykey/param /// param nameredisValue元素/param /// param nametype大于零 : 从表头开始向表尾搜索小于零 : 从表尾开始向表头搜索等于零移除表中所有与 VALUE 相等的值/param /// param namedb/param /// returns/returns public async Tasklong ListDelRangeAsync(string redisKey, string redisValue, long type 0, int db -1) { redisKey RenderKeyPrefix(redisKey); return await _database.ListRemoveAsync(redisKey, redisValue, type); } /// summary /// 清空List /// /summary /// param nameredisKey/param /// param namedb/param public async Task ListClearAsync(string redisKey, int db -1) { redisKey RenderKeyPrefix(redisKey); await _database.ListTrimAsync(redisKey, 1, 0); } } }Q1: RedisRepository 的设计优势是什么答 1. 封装性 封装了 StackExchange.Redis 的底层 API2. 易用性 提供了更简洁、更直观的方法3. 安全性 自动添加 Key 前缀避免冲突4. 灵活性 支持 String、Hash、List 等多种数据结构5. 性能 使用 Lua 脚本保证原子性 Q2: 如何保证 Redis 和数据库的一致性答 1. Cache-Aside Pattern 旁路缓存模式- 读先查缓存缓存未命中再查数据库- 写先写数据库再删除缓存2. 设置过期时间 保证最终一致性3. 主动更新 修改数据时主动清除相关缓存 Q3: Redis 的 Key 前缀有什么作用答 1. 多项目隔离 不同项目使用不同的前缀避免 Key 冲突2. 批量操作 可以按前缀批量删除某个项目的缓存3. 可维护性 通过前缀可以快速识别 Key 的来源 Q4: 为什么 ConnectionMultiplexer 要使用 Singleton答 1. 连接池共享 所有请求共享同一个连接池减少连接开销2. 性能最优 避免频繁创建和销毁连接3. 线程安全 ConnectionMultiplexer 内部实现了线程安全4. 自动重连 连接断开后自动重连RedisRepository 是一个封装了 StackExchange.Redis 的仓储类提供了 String、Hash、List 等多种数据结构的操作方法自动处理 Key 前缀隔离、对象序列化/反序列化、Lua 脚本原子性操作通过 ConnectionMultiplexer 实现连接池管理为项目提供高效、安全、易用的缓存服务。

相关新闻

《llama.cpp 与 GGML 生态下模型适配技术及应用研究报告》

《llama.cpp 与 GGML 生态下模型适配技术及应用研究报告》

llama.cpp 与 GGML 生态下模型适配技术及应用研究报告 核心摘要 llama.cpp 是 GGML 张量计算库 的主特性试验场与官方标准上层推理实现,采用「模型语义编排 → 张量算子图执行 → 多硬件后端调度」的分层架构,是目前跨平台兼容大模型推理的主流引擎。该…

2026/10/31 20:32:55 阅读更多 →
Java多线程Callable实用篇

Java多线程Callable实用篇

1、线程池的配置查看CompletableFuture2、业务场景为&#xff0c;只要有一任务失败&#xff0c;则所有任务都失败注意&#xff1a;线程池的拒绝策略为抛出异常。2.1、重写Callableclass MyCallable implements Callable<ThreadResult>{private final String params;priva…

2026/10/28 8:03:35 阅读更多 →
深度解析YimMenu:模块化安全防护架构与Lua扩展开发实战指南

深度解析YimMenu:模块化安全防护架构与Lua扩展开发实战指南

深度解析YimMenu&#xff1a;模块化安全防护架构与Lua扩展开发实战指南 【免费下载链接】YimMenu YimMenu, a GTA V menu protecting against a wide ranges of the public crashes and improving the overall experience. 项目地址: https://gitcode.com/GitHub_Trending/yi…

2026/10/30 13:55:35 阅读更多 →

最新新闻

Electron桌面应用开发实战:从环境搭建到打包发布全流程

Electron桌面应用开发实战:从环境搭建到打包发布全流程

做前端这些年&#xff0c;我大部分时间都在跟浏览器标签页打交道&#xff0c;直到第一次把桌面应用跑起来&#xff0c;才发现原来自己一直被限制在地址栏和刷新按钮围起来的框框里。Electron这个词&#xff0c;最开始是在某段时间频繁出现在同事的讨论里&#xff0c;一个用HTML…

2026/10/31 0:19:11 阅读更多 →
滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

滑盖智能锁T109深度解析:结构原理、安装调试与故障排查

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书是一份PDF格式的官方技术文档&#xff0c;主要面向智能锁安装人员、终端用户及售后维护者&#xff0c;帮助快速掌握产品安装、配置与常见故障处理。全包仅1个PDF文件&#xff0c;大小4.37MB&#xff0c;内容结构清晰&#xff…

2026/10/31 10:20:19 阅读更多 →
PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

PMSM弱磁控制原理与Simulink建模:高速AGV驱动系统设计指南

上个月我在调试一台高速AGV的驱动系统&#xff0c;整车要求空载跑到8m/s&#xff0c;电机转速已经拉到额定点&#xff0c;驱动器的输出电压也顶到了母线电压极限附近。再往上踩速度指令&#xff0c;转速纹丝不动&#xff0c;三相电流反而开始震荡。那会儿我的第一反应就是&…

2026/10/31 10:20:23 阅读更多 →
C++核心知识点:从内存管理到STL的实战指南

C++核心知识点:从内存管理到STL的实战指南

C 这门语言我前前后后折腾了十几年&#xff0c;用它写过嵌入式驱动、做过游戏服务端&#xff0c;也搭过基础组件库。每次看到新人一头扎进"C 核心知识点"这个搜索词&#xff0c;我其实挺感慨的——这门语言的难点从来不是语法多&#xff0c;而是知识点之间的耦合太深…

2026/10/31 0:59:28 阅读更多 →
T109滑盖智能锁怎么装?安装调试与故障排查全攻略

T109滑盖智能锁怎么装?安装调试与故障排查全攻略

简介&#xff1a;亚太天能T109滑盖智能锁产品说明书以PDF格式发布&#xff0c;适合智能锁用户、安装人员与售后维修人员阅读。内容围绕T109系列指纹密码刷卡锁展开&#xff0c;先介绍前面板、后面板、锁体、机械钥匙、电池盒盖等部件名称与功能&#xff0c;再给出完整安装步骤&…

2026/10/31 10:30:19 阅读更多 →
LLM知识降熵:从信息垄断到理解权下放

LLM知识降熵:从信息垄断到理解权下放

你有没有过这种经历&#xff1a;翻开源码注释、论文摘要或者一份专业报告&#xff0c;每个字都认识&#xff0c;连在一起却像天书。想找人聊聊&#xff0c;身边要么没人懂&#xff0c;要么懂的懒得跟你解释。后来我试着把同一段内容丢给一个大语言模型&#xff0c;几秒钟之后&a…

2026/10/31 9:29:43 阅读更多 →

日新闻

周新闻

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 18:03:41 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 11:00:40 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/31 0:09:01 阅读更多 →