Redis入门 - C#|.NET Core封装Nuget包

news2024/11/23 13:16:25

经过前面章节的学习,可以说大家已经算Redis开发入门了。已经可以去到项目上磨砺了。

但是今天我还想和大家分享一章:封装自己的Redis C#库,然后打包成Nuget包。

在这里插入图片描述

首先要说明的是:不是要自己开发一个Redis客户端库,而是基于上篇文章中介绍的6大库,做一个简单封装,真的很简单的那种,就是包个壳子。

再来说说为什么要做这个事情。

01原因

1.可测试

我希望代码是以服务的方式注入到程序中,而不是静态方法的方式去调用。使用依赖注入来提供服务使得程序可测试性增强,如果做单元测试,依赖注入的服务很容易通过mock来测试,而静态方法往往很难被模拟,测试起来很不灵活;

2.解耦

对于一个系统来说会使用到各种技术来达到某种能力,实现一个功能可能会有很多种方法,我们在意的是实现这个功能,而不是你用了什么方法。回到主题,我们需要的是使用Redis来实现业务功能,而具体用那个客户端库并不重要。

再举个例子比如今天我们选择了ServiceStack.Redis库接过遇到问题我们解决不了怎么办?难道业务不做了?不可能吧!而恰巧这个时候CSRedisCore库可以解决,你会怎么选?

这时候可能会想换的成本有多大?两种库方法名不统一,功能也不一样,如果系统中到处散落Redis方法调用,这可怎么换啊。

试想如果我们封装了一层,提供了一组接口,打包成Nuget包,这个时候大家用的就是这个Nuget包,而我们只需要把Nuget包里用ServiceStack.Redis实现的方法换成用CSRedisCore实现一下,大家直接更新一下Nuget包,可能一行代码都不用改就完成了替换。

因为我们依赖的是我们自己封装的接口,而不是具体Redis客户端库,因此可以解耦轻松替换。
在这里插入图片描述

3.扩展

Redis的原生功能可以理解为基础功能,表明Redis有这种能力。但是我们怎么使用,怎么更好的发挥它的价值,这就是我们自己能力的体现了。

大家相过想过没有,为什么有的库商业化做的特别好,特别简单容易上手。商业化好说明提供的服务好,也就是说明它能帮你做很多事情,它在原生功能上加了很多自己的创作。

项目做多了,我们自己也会遇到一些相似的功能,如果这个时候我们想把这些相似功能封装一下,要放哪里呢?怎么给别人用?如果我们遇到一个功能现有库都没有相应能力,需要我们基于原生自己开发实现又应该在哪做呢?

这些功能积累的多了总要有个地方放吧,而这时如果我们自己封装了一层,放哪的问题是不是一下子就解决了,说不定做着做着就成了一个产品了呢。

不知道到这里,大家有没有感觉思路一下子被打开了。可能我们没写几行代码,但是这个格局一下子就打开了,而这几行代码也可能产生意想不到的收获,可能是无限可能,也可能就是你实现自己Redis客户端库的开端。

在这里插入图片描述

当然这也是我自己对编码,对封装的个人拙见。说这么多也是希望可以给大家一些帮助。

02实现

下面闲话少说进入正题,如果来封装呢?

我们先梳理一下大致思路:

1.我们需要一个接口,里面包含:原库原生能力Client、其他我们自定义功能。

2.一个入口,别人要用,总要有个入口吧。

其实要求就这么简单。

下面我们就以封装CSRedisClient为例,首先定义IRedisService接口,里面包含Client字段以及两个演示的自定义方法

using CSRedis;
namespace Redis.RedisExtension
{
    public interface IRedisService
    {
        /// <summary>
        /// RedisClient
        /// </summary>
        /// <returns></returns>
        CSRedisClient Client { get; }
        #region 自定义方法
        /// <summary>
        /// 获取指定 key 的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        T Get<T>(string key);
        /// <summary>
        /// 获取指定 key 的值
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="key"></param>
        /// <returns></returns>
        Task<T> GetAsync<T>(string key);
        #endregion
    }
}

然后需要实现IRedisService,同时以CSRedisClient为构造函数入参,具体代码如下:

using CSRedis;
namespace Redis.RedisExtension
{
    public class RedisService : IRedisService
    {
        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="redisClient"></param>
        public RedisService(CSRedisClient redisClient)
        {
            Client = redisClient;
        }
        /// <summary>
        /// CSRedis
        /// </summary>
        /// <returns></returns>
        public CSRedisClient Client { get; }
        #region 自定义方法
        /// <summary>
        /// 获取指定 key 的值
        /// </summary>
        /// <returns></returns>
        public T Get<T>(string key)
        {
            return Client.Get<T>(key);
        }
        /// <summary>
        /// 获取指定 key 的值
        /// </summary>
        /// <returns></returns>
        public Task<T> GetAsync<T>(string key)
        {
            return Client.GetAsync<T>(key);
        }
        #endregion
    }
}

到这里第一个问题就解决了,对于第二个问题我们,我们可以对IServiceCollection进行扩展添加启动扩展方法AddRedisClientSetup,具体代码如下:

using CSRedis;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
namespace Redis.RedisExtension
{
    public static class RedisSetupExtensions
    {
        /// <summary>
        /// Redis客户端启动项
        /// </summary>
        /// <param name="services"></param>
        /// <returns></returns>
        public static IServiceCollection AddRedisClientSetup(this IServiceCollection services)
        {
            services.AddSingleton<CSRedisClient>(serviceProvider =>
            {
                var configuration = serviceProvider.GetRequiredService<IConfiguration>();
                var setting = configuration["RedisConnectionString"];
                return new CSRedisClient(setting);
            });
            services.AddSingleton<IRedisService, RedisService>();
            return services;
        }
        /// <summary>
        /// Redis客户端启动项
        /// </summary>
        /// <param name="services"></param>
        /// <param name="connectionString"></param>
        /// <returns></returns>
        public static IServiceCollection AddRedisClientSetup(this IServiceCollection services, string connectionString)
        {
            services.AddSingleton<CSRedisClient>(serviceProvider =>
            {
                return new CSRedisClient(connectionString);
            });
            services.AddSingleton<IRedisService, RedisService>();
            return services;
        }
    }
}

为什么要提供两个重载方法,因为如果用户基于我们的约定,在配置文件中以"RedisConnectionString"命名Redis连接字符串,用户直接调用AddRedisClientSetup()方法即可完成Redis启动,但是可能因为各种原因用户没法遵守约定,因此我们也要提供一个用户可以指定Redis连接字符串方法的入口。

下面我们就用基于约定的方式,在配置文件中加入以下配置:

{
  "RedisConnectionString": "127.0.0.1:6379"
}

然后使用Client.Set方法设置key1,再用自定义方法Get方法读取,代码如下:

public static void Run()
{
    var configuration = new ConfigurationBuilder()
        .SetBasePath(AppContext.BaseDirectory)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .Build();
    var services = new ServiceCollection();
    services.AddSingleton<IConfiguration>(configuration);
    services.AddRedisClientSetup();
    var redisService = services.BuildServiceProvider().GetService<IRedisService>();
    var setResult = redisService.Client.Set("key1", "value1");
    Console.WriteLine($"redisService.Client.Set(\"key1\",\"value1\")执行结果:{setResult}");
    var value = redisService.Get<string>("key1");
    Console.WriteLine($"redisService.Get<string>(\"key1\")执行结果:{value}");
    redisService.Client.Del("key1");
}

执行结果如下:

在这里插入图片描述

是不是很简单,然后我们只需要把上面三个文件IRedisService、RedisService、RedisSetupExtensions放到单独的类库中,然后打包发布成Nuget包,就可以给大家一起用啦,今天我这边就不发布Nuget包了,后面会有相关的计划,到时候再细聊。

:测试方法代码以及示例源码都已经上传至代码库,有兴趣的可以看看。https://gitee.com/hugogoos/Planner

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2130183.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【Linux】Linux常见指令以及权限理解(下)

【Linux】Linux常见指令以及权限理解&#xff08;下&#xff09; &#x1f955;个人主页&#xff1a;开敲&#x1f349; &#x1f525;所属专栏&#xff1a;Linux&#x1f34a; &#x1f33c;文章目录&#x1f33c; 3. Linux下基本指令 3.9 mv 指令&#xff08;重点&#xff0…

借助ChatGPT撰写学术论文的10条规则

在撰写学术论文时,利用ChatGPT等人工智能工具可以显著提高写作效率和质量。以下是结合ChatGPT功能,为构建学术论文提出的10条规则,包括详细的专业说明和格式化的GPT提示词。 规则1:明确论文目标与核心问题 专业说明:确定研究的核心问题和目标是撰写论文的第一步。这有助…

如何通过可视化大屏,助力智慧城市的“城市微脑”建设?

在智慧城市的宏伟蓝图中&#xff0c;常常面临着一个关键挑战&#xff1a;如何确保这些理念和技术能够真正地惠及城市的每一个角落&#xff0c;每一个产业&#xff0c;以及每一位市民。问题的核心在于城市的具体应用场景&#xff0c;无论是横向的社区、园区、镇街、学校、酒店、…

PCB绘制

01-2-PCB的通孔、盲孔、埋孔_哔哩哔哩_bilibili 通孔&#xff1a;起点和终点肯定是最后一层和第一层 盲孔&#xff1a;起点永远是第一层&#xff08;或最后一层&#xff09;&#xff0c;终点肯定不是最后一层&#xff08;或第一层&#xff09; 埋空&#xff1a;起点和终点肯…

建筑物检测系统源码分享

建筑物检测检测系统源码分享 [一条龙教学YOLOV8标注好的数据集一键训练_70全套改进创新点发刊_Web前端展示] 1.研究背景与意义 项目参考AAAI Association for the Advancement of Artificial Intelligence 项目来源AACV Association for the Advancement of Computer Visio…

Python | Leetcode Python题解之第401题二进制手表

题目&#xff1a; 题解&#xff1a; class Solution:def readBinaryWatch(self, turnedOn: int) -> List[str]:ans list()for i in range(1024):h, m i >> 6, i & 0x3f # 用位运算取出高 4 位和低 6 位if h < 12 and m < 60 and bin(i).count("1&…

C# USB通信技术(通过LibUsbDotNet库)

文章目录 1.下载LibusbDotNet库2.引入命名空间3. 实例化USB设备4.发送数据5.关闭连接 1.下载LibusbDotNet库 右击项目选择管理NuGet程序包在弹出的界面中搜索LibusbDotNet&#xff0c;然后下载安装。 2.引入命名空间 using LibUsbDotNet; using LibUsbDotNet.Main;3. 实例化…

细数H.264 H.265 H.266区别

H.264、H.265&#xff08;HEVC&#xff09;和H.266&#xff08;VVC&#xff09;是三种不同的视频编码标准&#xff0c;它们在压缩效率、图像质量、支持的分辨率以及技术特性等方面存在显著差异。以下是对这三种编码标准的详细比较&#xff1a; 概述 H.264&#xff1a;也称为AV…

TCP全连接队列和tcpdump抓包

全连接队列 listen第二个参数 服务器在调用listen的时候&#xff0c;listen的第二个参数 1&#xff0c;就是TCP全连接队列的长度。 当客户端的连接进入established 状态后&#xff0c;如果服务器没有调用accept将连接取走&#xff0c;那么该连接就会待在TCP全连接队列中&a…

Edge浏览器设置夜间模式/深色模式

问题背景 普通白色背景感觉有点刺眼&#xff0c;想改成深色背景&#xff0c;但是默认的设置里面只能修改边框的颜色&#xff1a; 这里虽然设置界面和边框变成了黑色的&#xff0c;但是实际上打开网页还是白色的。 全局配置 在Edge浏览器打开edge://flags/&#xff0c;然后搜索D…

海垦集团养殖场分布式光伏发电项目中的案例分享

1客户需求 海南农垦集团&#xff0c;原海南省农垦总公司&#xff0c;是中央直属三大垦区之一。集团在海南有多个养殖场&#xff0c;包括红华养猪场、红华肉牛繁育场等&#xff0c;计划在这些养殖场的屋顶安装分布式光伏系统&#xff0c;用于自发自用和余电上网。红华养猪场和红…

基于ESP32S3的链接大语言模型对话模块

本实物模块从实物外观、模块组成、API申请及功能说明四部分来介绍这款基于ESP32S3的大语言模型对话模块。 1、实物外观 2、模块介绍 本硬件平台主要由三个模块组成&#xff0c;包括MAX9814录音模块、MAX98357音频功放模块和ESP32S3模块。如下图所示。 MAX9814录音模块&#…

mac 如何开启指定端口供外部访问?

前言 需要 mac 上开放指定端口&#xff0c;指定 ip 访问 解决 在 macOS 上开放一个端口&#xff0c;并指定只能特定的 IP 访问&#xff0c;可以使用 macOS 内置的 pfctl(Packet Filter)工具来实现。 1、 编辑 pf 配置文件&#xff1a; 打开 /etc/pf.conf 文件进行编辑。 可以使…

深度解读MySQL意向锁的工作原理机制与应用场景

意向锁 意向锁的概念 意向锁是InnoDB自动添加的一种锁&#xff0c;不需要用户去干预。 是数据库中的一种表级锁&#xff0c;一个事务要给一个资源加锁时&#xff0c;必须要先获取到对应类型的意向锁之后&#xff0c;才可以给这个资源加上自己想要的共享锁或者排他锁&#xff0…

【C++ 面试 - 新特性】每日 3 题(十一)

✍个人博客&#xff1a;Pandaconda-CSDN博客 &#x1f4e3;专栏地址&#xff1a;http://t.csdnimg.cn/fYaBd &#x1f4da;专栏简介&#xff1a;在这个专栏中&#xff0c;我将会分享 C 面试中常见的面试题给大家~ ❤️如果有收获的话&#xff0c;欢迎点赞&#x1f44d;收藏&…

电容器制造5G智能工厂物联数字孪生平台,推进制造业数字化转型

在当今全球制造业的数字化转型浪潮中&#xff0c;电容器制造业作为电子元件的关键领域&#xff0c;正积极拥抱5G智能工厂与物联数字孪生平台技术&#xff0c;以推动生产效率的飞跃和产品质量的提升。这一创新模式不仅为电容器制造业注入了新的活力&#xff0c;也为整个制造业的…

植物三萜皂苷生物合成途径及调控机制研究进展-文献精读48

摘要 三萜皂苷(triterpenoids saponins)是由三萜皂苷元和一个或多个糖基和/或其他化学基团缩合而成的一系列结构多样的天然化合物[1], 主要分布在五加科、蝶形花科、石竹科、桔梗科、毛茛科、玄参科、葫芦科等植物中[2]. 植物中三萜皂苷常分布在特定的器官和组织, 如人参(Pana…

TypeScript中 unknown类型的变量值赋值给具体类型的变量

将unknown类型分配给string类型&#xff0c;但我就想给y 赋值&#xff0c;提供了几个方法 // unknown 是类型安全的any let x : unknown; x 1; x true; x sdfj;let y : string; // y x;//不能将unknown类型分配给string类型&#xff0c;但我就想给y 赋值 &#xff0c;下方…

Matlab如何配置小波工具(Wavelet Toolbox)

1、发现问题 因为实验要使用小波工具函数&#xff0c;运行时报错如下&#xff1a; 查看对应文件夹发现没有小波工具&#xff08;也可在控制台输入ver&#xff09;&#xff0c;检查是否有该工具&#xff0c;输入后回车返回如下&#xff1a; 2、下载工具包 没有这个工具就要去下…

信息安全数学基础(8)整数分解

前言 在信息安全数学基础中&#xff0c;整数分解是一个核心概念&#xff0c;它指的是将一个正整数表示为几个正整数的乘积的形式。虽然对于任何正整数&#xff0c;理论上都可以进行分解&#xff08;除了1只能分解为1本身&#xff09;&#xff0c;但整数分解在密码学和信息安全中…