火影 发表于 2024-8-7 03:22:35

从0到1,为ASP.NET Core项目添加redis支持(全程使用copilot编码)

提示:文章写完后,目次可以自动生成,怎样生成可参考右边的帮助文档


前言

最近做需求需要用到redis,之前只在java用过,c#的使用还不清楚,故总结记录一下工作流。全程使用copilot进行代码编写
一、需求背景

需求是一个简朴的获取token的需求,token获取需要经过http调用第三方系统,故为了减少http调用,设置redis来缓存token数据。
二、扣问copilot工作流

1.简朴扣问

上来就先简朴问问,怎样导入redis:
https://img-blog.csdnimg.cn/direct/d7db533714194ea08c33f29132c74da4.png
可以看到,由于是刚开始发起提问,没有上下文,它给我整个了python的,我需要c#的,故继续提问。
https://img-blog.csdnimg.cn/direct/c6fc439cc97f4c9283fe2b949b503b13.png
这次是c#的方法了,但是更类似那种本身测试来玩玩的写法, 不符合我们的ASP.NET Core项目需求,故再次详细提问。
https://img-blog.csdnimg.cn/direct/f5851a3157ad48d2b4433acc46077b54.png
https://img-blog.csdnimg.cn/direct/10f1da03e049403ea22ab5a681634e02.png
https://img-blog.csdnimg.cn/direct/a3a3fc8b8c914a7d99c58ec7cefbe6ff.png
可以看到,这次的回答就比力详细了,从appsetting配置,到startup的注入,到现实代码里面的使用(他这里是直接在controller里面使用,我们一样平常照旧建个RedisHeloer工具类来统一使用,后面再详细扣问这方面)都有给出。至此,理论上是可以完备的测试并使用redis了,但是,我们还需相识更多细节才气应用到本身的代码里。
2.细节扣问

我们可以注意到,固然上一步中给出了大体使用步骤,但是还有一些地方的细节是含糊不清的,好比这个redis的ConnectionString,就写得比力不清不楚,一个localhost,显然不能满足需求
https://img-blog.csdnimg.cn/direct/8bf1ea2bff9a4d34841fb6a56982af43.png
故就此细节再次进行提问:
https://img-blog.csdnimg.cn/direct/2b559713e91049c49d3c5ee4ec721c78.png
https://img-blog.csdnimg.cn/direct/4e7e2f04de4743868494ccc7a7d318a0.png
这次的回答就比力详细了,从中我们可知redis链接串还可以指定端标语,指定暗码,指定命据库等属性,终极我们也是直接采用了它给出的完备配置。
至此,对应到我们的项目中,已经添加了如下代码:
首先是appsetting文件,界说了链接串:
"Redis": {
    "ConnectionString": "localhost:6379,password=yourpassword,ssl=False,abortConnect=False,defaultDatabase=0"
}
然后是startup文件,界说了redis链接的注入,以及我们redis工具类的注入:
    public static void ConfigureServices(IServiceCollection services,
      IConfigurationManager configuration)
    {

      // redis setting
      var redisConnectionString = configuration.GetSection("Redis:ConnectionString").Value;
      services.AddSingleton<IConnectionMultiplexer>(ConnectionMultiplexer.Connect(redisConnectionString));
      services.AddScoped<RedisHelper>();

    }
完成基础的配置跟注入后,就是详细的功能方法的提问,
https://img-blog.csdnimg.cn/direct/215af47e9a8a4895bf4c71cee9fcef2a.png
https://img-blog.csdnimg.cn/direct/57eb5f11e0a64b56be563c17f2f3f452.png
现在我需要一个方法,可以做到redis有值的时间取值,无值的时间调用我传入的方法获取值,并set到redis去。可以看到,copilot给我返回了一个代码,但是注意,里面涉及了序列化与反序列化方法,并没有实现,我们可以继续就此追问。
https://img-blog.csdnimg.cn/direct/816709677803491ca2ffeb9fc5c15672.png
https://img-blog.csdnimg.cn/direct/e1a5d885130c48bdbd1689965664e90f.png
这里它 给出了两种实现json序列化与反序列化的两种方式,System.Text.Json 和 Newtonsoft.Json ,我这里使用 Newtonsoft.Json 。
同时,我们设置rediskey跟value的时间,需要思量逾期时间,故继续扣问怎样设置逾期时间。
https://img-blog.csdnimg.cn/direct/36367fb88cec46488c2994c7adbad76a.png
https://img-blog.csdnimg.cn/direct/8e6bdd94c8b9491e96c19c2cc201cf7f.png
可以看到,我们可以用StringSet方法来设置逾期时间,它接收TimeSpan范例的参数。为了代码方便管理,像redis的逾期时间,还有前面提到的rediskey,一样平常都是界说好常量,直接使用,而TimeSpan范例,由于不能设置成常量,以是我们把常量界说为逾期时间的数值。
至此,对应到我们的项目中,又新添加了如下代码:
界说常量的静态类,界说了key跟逾期时间。逾期时间设置为3500s,是因为我们调用http接口获取的token逾期时间是3600s,这样能包管让时间全覆盖
    public static class RedisConstants
    {
      public static class RedisKeys
      {
            public const string CRM_ADMIN_TOKEN_KEY = "crm_admin_token_key";
      }

      public static class RedisExpireTime
      {
            // 3500s
            public const int CRM_ADMIN_TOKEN_EXPIRE_TIME = 3500;
      }
    }
界说redis调用的RedisHelper工具类:
    public class RedisHelper
    {
      private readonly IDatabase _database;

      public RedisHelper(IConnectionMultiplexer connectionMultiplexer)
      {
            _database = connectionMultiplexer.GetDatabase();
      }

      public T GetOrSetValue<T>(string key, int expirtime, Func<T> valueFactory)
      {
            // 尝试从Redis获取值
            var value = _database.StringGet(key);
            if (value.HasValue)
            {
                Console.WriteLine("Get value from Redis!");
                // 如果存在,反序列化并返回
                return Deserialize<T>(value);
            }
            else
            {
                Console.WriteLine("redis not value , Get value from Factory!");
                // 如果不存在,调用valueFactory获取值
                var newValue = valueFactory();
                // 序列化并存储到Redis
                _database.StringSet(key, Serialize(newValue), TimeSpan.FromSeconds(expirtime));
                return newValue;
            }
      }

      private byte[] Serialize<T>(T value)
      {
            // 实现序列化逻辑,根据实际情况选择合适的序列化方式
            var jsonString = JsonConvert.SerializeObject(value);
            return System.Text.Encoding.UTF8.GetBytes(jsonString);
      }

      private T Deserialize<T>(RedisValue value)
      {
            // 实现反序列化逻辑,根据实际情况选择合适的反序列化方式
            var jsonString = System.Text.Encoding.UTF8.GetString(value);
            return JsonConvert.DeserializeObject<T>(jsonString);
      }
    }
3.代码定制化

至此,copilot已经给出一套完备的demo了,但是,还不敷。毕竟copilot无法直接读取整个项目标上下文,只能根据提问给出一些比力通用的办理方案,我们还要思量本身的项目本身,怎样让copilot给出的建议代码更好的融入我们的项目。还记得我之前提到的http调用的方法吗?该方法是一个异步的方法,通过http调用来获取token。
该获取token的方法署名如下
public async Task<string> GetAdminTokenFromCRM(string authParam)
而copilot建议的代码如下
public T GetOrSetValue<T>(string key, int expirtime, Func<T> valueFactory)
这个传入的方法并不是异步方法,且GetOrSetValue方法也不是异步的,故需要进行改造,一开始我按本身的想法来改,但是照旧比力缺少对异步编程这块的知识,报了几个错。于是把代码圈中扣问copilot
https://img-blog.csdnimg.cn/direct/8c8a08ac8c7147d9b80fc63d34def4e7.png
https://img-blog.csdnimg.cn/direct/f3b768ec3dc0474c86c12818580d4553.png
可以看到,copilot已经针对我错误的改造给出了建议。
同时也让我相识到Task的一些相关知识,在此概括总结下:
1.假如用Task界说了一个异步方法A,那么调用这个方法A的方法,以及所有的上层方法都要用 async Task 来界说
2.调用异步方法时,要在前面加 await 关键字
3.异步方法内部可以调同步方法,调用同步方法时无需加 await 关键字
至此完成了代码的定制化
终极,对应到我们的项目中,又新添加,或者更改了如下代码:
AlthHelper类,
    public class CRMAuthHelper
    {
      private readonly CRMHttpHelper _cRMHttpHelper;
      private readonly RedisHelper _redisHelper;

      public CRMAuthHelper(CRMHttpHelper cRMHttpHelper,RedisHelper redisHelper)
      {
            _cRMHttpHelper = cRMHttpHelper;
            _redisHelper = redisHelper;
      }

      public async Task<string> GetAdminiToken(string authParam)
      {
            string token = await GetFromRedis(authParam);
            return token;
      }

      public async Task<string> GetFromRedis(string authParam)
      {
            string token = await _redisHelper.GetOrSetValue<string>(RedisConstants.RedisKeys.CRM_ADMIN_TOKEN_KEY,
                RedisConstants.RedisExpireTime.CRM_ADMIN_TOKEN_EXPIRE_TIME,
                () => _cRMHttpHelper.GetAdminTokenFromCRM(authParam));
            return token;
      }
    }
更改后的RedisHelper工具类
    public class RedisHelper
    {
      private readonly IDatabase _database;

      public RedisHelper(IConnectionMultiplexer connectionMultiplexer)
      {
            _database = connectionMultiplexer.GetDatabase();
      }

      public async Task<T> GetOrSetValue<T>(string key, int expirtime, Func<Task<T>> valueFactory)
      {
            // 尝试从Redis获取值
            var value = _database.StringGet(key);
            if (value.HasValue)
            {
                Console.WriteLine("Get value from Redis!");
                // 如果存在,反序列化并返回
                return Deserialize<T>(value);
            }
            else
            {
                Console.WriteLine("redis not value , Get value from Factory!");
                // 如果不存在,调用valueFactory获取值
                var newValue = await valueFactory();
                // 序列化并存储到Redis
                _database.StringSet(key, Serialize(newValue), TimeSpan.FromSeconds(expirtime));
                return newValue;
            }
      }

      private byte[] Serialize<T>(T value)
      {
            // 实现序列化逻辑,根据实际情况选择合适的序列化方式
            var jsonString = JsonConvert.SerializeObject(value);
            return System.Text.Encoding.UTF8.GetBytes(jsonString);
      }

      private T Deserialize<T>(RedisValue value)
      {
            // 实现反序列化逻辑,根据实际情况选择合适的反序列化方式
            var jsonString = System.Text.Encoding.UTF8.GetString(value);
            return JsonConvert.DeserializeObject<T>(jsonString);
      }
    }
下面是调用获取token时的局部代码,仅需调用AuthHelper即可
            // get adminauth
            string accesstoken = await _authHelper.GetAdminiToken(cRMSystemConfig.AuthParam);
三、终极验证

下面验证一下,首先是初次调用时,确实走到了http调用获取token的方法
https://img-blog.csdnimg.cn/direct/e56b988bef7a4c72a40118c004ffb7f8.png
第二次再调用,则会直接使用redis里面的值
https://img-blog.csdnimg.cn/direct/694fee9d55424acca2bdc568c01e9fda.png
总结

以上就是本次copilot的使用工作流,在此总结一下:
1.对于自身的需求,可以先抛出简朴的提示词
2.再根据copilot的回答进一步引导提问并优化细节
3.针对终极的建议代码,再次进行定制化提问,以顺应已有项目
每日一遍,copilot真是太好用啦!

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
页: [1]
查看完整版本: 从0到1,为ASP.NET Core项目添加redis支持(全程使用copilot编码)