.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/7/30 20:05:46 阅读更多 →
Java多线程Callable实用篇

Java多线程Callable实用篇

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

2026/7/30 20:05:12 阅读更多 →
深度解析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/7/30 19:35:05 阅读更多 →

最新新闻

robinhood-node期权交易完全指南:从理论到实践的完整路径

robinhood-node期权交易完全指南:从理论到实践的完整路径

robinhood-node期权交易完全指南&#xff1a;从理论到实践的完整路径 【免费下载链接】robinhood-node :chart_with_upwards_trend: NodeJS client for Robinhood Trading :fire: 项目地址: https://gitcode.com/gh_mirrors/ro/robinhood-node robinhood-node是一个强大…

2026/7/30 21:42:50 阅读更多 →
【题解-信息学奥赛一本通】1373:鱼塘钓鱼(fishing)

【题解-信息学奥赛一本通】1373:鱼塘钓鱼(fishing)

题目&#xff1a;1373&#xff1a;鱼塘钓鱼(fishing&#xff09; 题目描述 有N个鱼塘排成一排&#xff08;N<100&#xff09;&#xff0c;每个鱼塘中有一定数量的鱼&#xff0c;例如&#xff1a;N5时&#xff0c;如下表&#xff1a; 鱼塘编号每1分钟能钓到的鱼的数量&…

2026/7/30 21:42:50 阅读更多 →
聊天机器人、AI助手、AI智能体到底有什么区别?搞懂这层才算真正入门

聊天机器人、AI助手、AI智能体到底有什么区别?搞懂这层才算真正入门

聊天机器人、AI助手、AI智能体到底有什么区别&#xff1f;搞懂这层才算真正入门基于规则或简单模型的问答系统&#xff0c;主要用于FAQ检索和客服首轮分流&#xff0c;比如“怎么退货”“营业时间”这类高频标准问题。遇到超出预设范围的问题&#xff0c;它只能说“这个我不太清…

2026/7/30 21:42:50 阅读更多 →
7.Java 深度练习

7.Java 深度练习

Java程序的基本结构记住这个模板&#xff0c;以后每个Java文件都长这样&#xff1a;public class‌&#xff1a;声明一个公共类&#xff0c;public表示这个类可以被其他任何类访问&#xff1b;class是定义类的关键字。HelloWorld‌&#xff1a;这是类名。根据 Java 规范&#x…

2026/7/30 21:42:50 阅读更多 →
Seed-VC模型选择实战:从入门到精通的完整指南

Seed-VC模型选择实战:从入门到精通的完整指南

Seed-VC模型选择实战&#xff1a;从入门到精通的完整指南 【免费下载链接】seed-vc zero-shot voice conversion & singing voice conversion, with real-time support 项目地址: https://gitcode.com/GitHub_Trending/se/seed-vc Seed-VC是一个强大的零样本语音转换…

2026/7/30 21:42:50 阅读更多 →
无人驾驶车辆侧偏刚度估算与RLS算法应用

无人驾驶车辆侧偏刚度估算与RLS算法应用

1. 无人驾驶车辆侧偏刚度估算的核心挑战在无人驾驶车辆动力学控制领域&#xff0c;侧偏刚度&#xff08;Cornering Stiffness&#xff09;的准确估算一直是个棘手问题。这个参数直接决定了车辆在转弯时的轮胎力特性&#xff0c;就像人的鞋子与地面的摩擦系数会影响跑步稳定性一…

2026/7/30 21:41:50 阅读更多 →

日新闻

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具:DriverStoreExplorer完全指南

Windows驱动存储终极清理工具&#xff1a;DriverStoreExplorer完全指南 【免费下载链接】DriverStoreExplorer Driver Store Explorer 项目地址: https://gitcode.com/gh_mirrors/dr/DriverStoreExplorer 您是否曾因Windows系统盘空间不足而烦恼&#xff1f;是否遇到过设…

2026/7/30 0:00:13 阅读更多 →
如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper:网页视频下载的完整实战指南

如何3步掌握Video Download Helper&#xff1a;网页视频下载的完整实战指南 【免费下载链接】VideoDownloadHelper Chrome Extension to Help Download Video for Some Video Sites. 项目地址: https://gitcode.com/gh_mirrors/vi/VideoDownloadHelper 你是否曾经在浏览…

2026/7/30 0:00:13 阅读更多 →
“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

“双减”后首个AI备课压力测试报告:覆盖32所中小学的176节AI辅助课,暴露4大隐性增负节点

更多请点击&#xff1a; https://intelliparadigm.com 第一章&#xff1a;AI 教师备课辅助 AI 教师备课辅助系统正逐步成为教育数字化转型的核心支撑工具&#xff0c;它并非替代教师&#xff0c;而是通过语义理解、知识图谱与多模态生成能力&#xff0c;将教师从重复性劳动中解…

2026/7/30 0:00:13 阅读更多 →

周新闻

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 道路桥梁裂缝检测数据集 道路桥梁病害识别检测数据集

深度学习道路桥梁裂缝检测系统 数据集6000张 完整源码已标注数据集训练好的模型环境配置教程程序运行说明文档&#xff0c;可以直接使用&#xff01;系统支持图片、视频、摄像头等多种方式检测裂缝&#xff0c;功能强大实用。 1数据集6000张 8各类别

2026/7/29 22:18:20 阅读更多 →
深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

深度学习YOLO模型如何训练 PUBG 绝地求生目标检测数据集

pubg数据集 精选原图1.42万数据 1.49万标签 无任何重复、算法增强或冗余图像&#xff01; pubg绝地求生目标检测数据集 1分类&#xff1a;e_body&#xff0c;14905个标签&#xff0c;txt格式 共计14244张图&#xff0c;99%为640*640尺寸图像 适合yolo目标检测、AI训练关键词&am…

2026/7/29 14:34:28 阅读更多 →
Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex英雄目标检测数据集 深度学习框架YOLO如何训练APEX数据集

Apex检测数据集数据集详情检测类别&#xff1a; allies enemy tag图片总量&#xff1a;7247张训练集&#xff1a;5139张验证集&#xff1a;1425张测试集&#xff1a;683张标注状态&#xff1a;全部已标注&#xff0c;即拿即用数据格式&#xff1a;支持YOLO格式及其他格式&#…

2026/7/29 15:00:03 阅读更多 →

月新闻