net6使用StackExchangeRedis实现分布式缓存

上一篇讲解了Redis的搭建及ServiceStack.Redis 与 StackExchange.Reids 的区别https://blog.csdn.net/qq_39569480/article/details/105249607

这篇文章遗我们来说下使用Microsoft.Extensions.Caching.StackExchangeRedis来对redis进行操作及帮助类。

首先在windows上安装redis 在上边连接中有教程 .

创建webapi项目
在这里插入图片描述

在包管理器中安装Microsoft.Extensions.Caching.StackExchangeRedis
在这里插入图片描述
在appsettings.json中添加连接字符串:(redis连接字符串)
ps:这里需要特别注意,与普通链接redis不同的是,如果我们想指定数据库名称,不能直接在连接字符串中追加,需要在Program中指定。

  "ConnectionStrings": {
    "Redis": "123.123.33.22:6379,password=123456!,ConnectTimeout=15000,SyncTimeout=5000"
  },

定义一个接口

public interface ICache
{
    #region 设置缓存 
    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">值</param>
    void SetCache(string key, object value);
    /// <summary>
    /// 设置缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">值</param>
    Task SetCacheAsync(string key, object value);

    /// <summary>
    /// 设置缓存
    /// 注:默认过期类型为绝对过期
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">值</param>
    /// <param name="minutes">过期时间间隔 以分钟为单位</param>
    void SetCache(string key, object value,int minutes);

    /// <summary>
    /// 设置缓存
    /// 注:默认过期类型为绝对过期
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">值</param>
    /// <param name="timeout">过期时间间隔 以分钟为单位</param>
    Task SetCacheAsync(string key, object value, int minutes);

    /// <summary>
    /// 设置缓存
    /// 注:默认过期类型为绝对过期
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">值</param>
    /// <param name="minutes">过期时间间隔 以分钟为单位</param>
    /// <param name="expireType">过期类型</param>  
    void SetCache(string key, object value, int minutes, ExpireType expireType);

    /// <summary>
    /// 设置缓存
    /// 注:默认过期类型为绝对过期
    /// </summary>
    /// <param name="key">缓存Key</param>
    /// <param name="value">值</param>
    /// <param name="minutes">过期时间间隔 以分钟为单位</param>
    /// <param name="expireType">过期类型</param>  
    Task SetCacheAsync(string key, object value, int minutes, ExpireType expireType);
    #endregion

    #region 获取缓存

    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    string GetCache(string key);

    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    Task<string> GetCacheAsync(string key);

    /// <summary>
    /// 获取缓存,没有则添加
    /// </summary>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="minutes"></param>
    /// <param name="expireType">过期类型,默认绝对过期</param>
    /// <returns></returns>
    Task<string> GetOrAddAsync(string key, object value, int minutes, ExpireType expireType = 0);
    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    T GetCache<T>(string key);
    /// <summary>
    /// 获取缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    Task<T> GetCacheAsync<T>(string key);

    /// <summary>
    /// 获取泛型缓存,没有则添加
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="value"></param>
    /// <param name="minutes"></param>
    /// <param name="expireType">过期类型,默认绝对过期</param>
    /// <returns></returns>
    Task<T> GetOrAddAsync<T>(string key, Func<Task<T>> valueFactory, int minutes, ExpireType expireType = 0);
    /// <summary>
    /// 获取泛型集合缓存,没有则添加
    /// </summary>
    /// <typeparam name="T"></typeparam>
    /// <param name="key"></param>
    /// <param name="valueFactory"></param>
    /// <param name="minutes"></param>
    /// <param name="expireType"></param>
    /// <returns></returns>
    Task<List<T>> GetOrAddListAsync<T>(string key, Func<Task<List<T>>> valueFactory, int minutes, ExpireType expireType = 0);

    #endregion

    #region 删除缓存

    /// <summary>
    /// 清除缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    void RemoveCache(string key);

    /// <summary>
    /// 清除缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    Task RemoveCacheAsync(string key);

    #endregion

    #region 刷新缓存
    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    void RefreshCache(string key);
    /// <summary>
    /// 刷新缓存
    /// </summary>
    /// <param name="key">缓存Key</param>
    Task RefreshCacheAsync(string key);
    #endregion
}

实现接口中定义的方法

public class CacheHelper : ICache
{
    readonly IDistributedCache _cache;

    public CacheHelper(IDistributedCache cache)
    {
        _cache = cache;
    }

    protected string BuildKey(string idKey)
    {
        return $"Cache_{idKey}";
    }
    public void SetCache(string key, object value)
    {
        string cacheKey = BuildKey(key);
        _cache.SetString(cacheKey, value.ToJson());
    }

    public async Task SetCacheAsync(string key, object value)
    {
        string cacheKey = BuildKey(key);
        await _cache.SetStringAsync(cacheKey, value.ToJson());
    }

    public void SetCache(string key, object value,int minutes)
    {
        string cacheKey = BuildKey(key);
        _cache.SetString(cacheKey, value.ToJson(), new DistributedCacheEntryOptions
        {
            AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
        });
    }

    public async Task SetCacheAsync(string key, object value, int minutes)
    {
        string cacheKey = BuildKey(key);
        await _cache.SetStringAsync(cacheKey, value.ToJson(), new DistributedCacheEntryOptions
        {
            AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
        });
    }

    public void SetCache(string key, object value, int minutes, ExpireType expireType)
    {
        string cacheKey = BuildKey(key);
        if (expireType == ExpireType.Absolute)
        {
            //这里没转换标准时间,Linux时区会有问题?
            _cache.SetString(cacheKey, value.ToJson(), new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
            });
        }
        else
        {
            _cache.SetString(cacheKey, value.ToJson(), new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(minutes)
            });
        }
    }

    public async Task SetCacheAsync(string key, object value, int minutes, ExpireType expireType)
    {
        string cacheKey = BuildKey(key);
        if (expireType == ExpireType.Absolute)
        {
            //这里没转换标准时间,Linux时区会有问题?
            await _cache.SetStringAsync(cacheKey, value.ToJson(), new DistributedCacheEntryOptions
            {
                AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
            });
        }
        else
        {
            await _cache.SetStringAsync(cacheKey, value.ToJson(), new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(minutes)
            });
        }
    }

    public string GetCache(string idKey)
    {
        if (idKey.IsNullOrEmpty())
        {
            return null;
        }
        string cacheKey = BuildKey(idKey);
        var cache = _cache.GetString(cacheKey);
        return cache;
    }
    public async Task<string> GetCacheAsync(string key)
    {
        if (key.IsNullOrEmpty())
        {
            return null;
        }
        string cacheKey = BuildKey(key);
        var cache = await _cache.GetStringAsync(cacheKey);
        return cache;
    }
    public async Task<string> GetOrAddAsync(string key,object value,int minutes, ExpireType expireType=0)
    {
        if (key.IsNullOrEmpty()) return null;

        string cacheKey = BuildKey(key);
        var cache = await _cache.GetStringAsync(cacheKey);
        if (cache==null)
        {
            string json= value.ToJson();
            if (expireType == ExpireType.Absolute)
            {
                //这里没转换标准时间,Linux时区会有问题?
                await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
                {
                    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
                });
            }
            else
            {
                await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(minutes)
                });
            }
            return json;
        }
        return cache;
    }

    public T GetCache<T>(string key)
    {
        var cache = GetCache(key);
        if (!cache.IsNullOrEmpty())
        {
            return cache.ToObject<T>();
        }
        return default(T);
    }

    public async Task<T> GetCacheAsync<T>(string key)
    {
        var cache = await GetCacheAsync(key);
        if (!string.IsNullOrEmpty(cache))
        {
            return cache.ToObject<T>();
        }
        return default(T);
    }
    public async Task<T> GetOrAddAsync<T>(string key, Func<Task<T>> valueFactory, int minutes, ExpireType expireType = 0)
    {
        if (key.IsNullOrEmpty()) return default(T);

        string cacheKey = BuildKey(key);
        var cache = await _cache.GetStringAsync(cacheKey);
        
        if (cache == null)
        {//缓存中没有数据去查实际的数据
            T value = await valueFactory();
            string json = value.ToJson();
            //如果查实际的数据还是没有那么直接返回空
            if (string.IsNullOrWhiteSpace(json)) return cache.ToObject<T>();

            if (expireType == ExpireType.Absolute)
            {
                //这里没转换标准时间,Linux时区会有问题?
                await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
                {
                    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
                });
            }
            else
            {
                await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(minutes)
                });
            }
            return json.ToObject<T>();
        }
        return cache.ToObject<T>();
    }
    public async Task<List<T>> GetOrAddListAsync<T>(string key, Func<Task<List<T>>> valueFactory, int minutes, ExpireType expireType = 0)
    {
        if (key.IsNullOrEmpty()) return default(List<T>);

        string cacheKey = BuildKey(key);
        var cache = await _cache.GetStringAsync(cacheKey);

        if (cache == null)
        {//缓存中没有数据去查实际的数据
            List<T> value = await valueFactory();
            string json = value.ToJson();
            //如果查实际的数据还是没有那么直接返回空
            if (string.IsNullOrWhiteSpace(json)) return cache.ToObject<List<T>>();

            if (expireType == ExpireType.Absolute)
            {
                //这里没转换标准时间,Linux时区会有问题?
                await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
                {
                    AbsoluteExpiration = DateTimeOffset.Now.AddMinutes(minutes)
                });
            }
            else
            {
                await _cache.SetStringAsync(cacheKey, json, new DistributedCacheEntryOptions
                {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(minutes)
                });
            }
            return json.ToObject<List<T>>();
        }
        return cache.ToObject<List<T>>();
    }

    public void RemoveCache(string key)
    {
        _cache.Remove(BuildKey(key));
    }

    public async Task RemoveCacheAsync(string key)
    {
        await _cache.RemoveAsync(BuildKey(key));
    }

    public void RefreshCache(string key)
    {
        _cache.Refresh(BuildKey(key));
    }

    public async Task RefreshCacheAsync(string key)
    {
        await _cache.RefreshAsync(BuildKey(key));
    }
}

定义一个枚举的过期类型

public enum ExpireType
{
    /// <summary>
    /// 绝对过期
    /// 注:即自创建一段时间后就过期
    /// </summary>
    Absolute,

    /// <summary>
    /// 相对过期
    /// 注:即该键未被访问后一段时间后过期,若此键一直被访问则过期时间自动延长
    /// </summary>
    Relative,
}

定义一个string的扩展方法

public static class ValueToObject
{
    /// <summary>
    /// 将Json字符串反序列化为对象
    /// </summary>
    /// <typeparam name="T">对象类型</typeparam>
    /// <param name="jsonStr">Json字符串</param>
    /// <returns></returns>
    public static T ToObject<T>(this string jsonStr)
    {
        return JsonConvert.DeserializeObject<T>(jsonStr);
    }
    /// <summary>
    /// 将字符串序列化为json
    /// </summary>
    /// <param name="str"></param>
    /// <returns></returns>
    public static string ToJson(this object str)
    {
        return JsonConvert.SerializeObject(str);
    }
}

在Program.cs文件中注入并添加redis

builder.Services.AddScoped(typeof(ICache), typeof(CacheHelper));//注入
var redisOptions = ConfigurationOptions.Parse(builder.Configuration.GetConnectionString("Redis"));
redisOptions.DefaultDatabase = 1; // 选择数据库索引为1的数据库,默认是0 
builder.Services.AddStackExchangeRedisCache(o =>
{
    //redis连接
    o.ConfigurationOptions = redisOptions;//指定数据库
    //o.Configuration = builder.Configuration.GetConnectionString("Redis");不指定数据库可以直接连接
    //设置缓存key的前缀
    //o.InstanceName = "";
});

控制器中使用

[Route("demo")]
[ApiController]
//[Authorize]
public class DemoController : ControllerBase
{
    private readonly IDemoService _demo;
    public DemoController(IDemoService demo)
    {
        _demo = demo;
    }

    /// <summary>
    /// 获取详情
    /// </summary>
    /// <param name="input"></param>
    /// <returns></returns>
    [HttpGet]
    public async Task<dynamic> Get(int id)
    {
         DemoGetOutPut demo=await _cache.GetOrAddAsync<DemoGetOutPut>($"demo{id}", async () => await GetDemo(id),20);
        return demo;
    }
    
    public async Task<dynamic> GetDemo(int id)
    {
        return new { name= "张三", age= 18 };
    }
}
StackExchange.Redis,这是redis 的.net客户端之一。Redis是一个开源的内存数据存储,可以用来做数据库,缓存或者消息代理服务。目前有不少人在使用    ServiceStack.Redis这个.net客户端,但是这个的最新版本目前已经变成了商业软件。对于    ServiceStack.Redis这种行为,我们没有什么好说的,留给我们的选择是使用低版本的开源版本或者转向其他的客户端。要说到StackExchange.Redis,就不得不说它和BookSleeve的关系。BookSleeve已经是比较完善的redis sdk,但是为什么 BookSleeve 的作者要重新写一个redis 的客户端sdk呢? 有兴趣的同学可以看这里    why i wrote another redis client归纳起来其实就一句话:觉得不爽就推倒重来。StackExchange.Redis 安装直接命令或者手动NuGet。 PM> Install-Package StackExchange.Redis 如果需要强签名的版本走下面的命令,当然作者对于强签名的事也是充满了    怨念  PM> Install-Package StackExchange.Redis.StrongName ConnectionMultiplexerConnectionMultiplexer对象是StackExchange.Redis最中枢的对象。这个类的实例需要被整个应用程序域共享和重用的,你不要在每个操作中不停的创建该对象的实例,所以使用单例来创建和存放这个对象是必须的。public static ConnectionMultiplexer Manager     {         get         {             if (_redis == null)             {                 lock (_locker)                 {                     if (_redis != null) return _redis;                     _redis = GetManager();                     return _redis;                 }             }             return _redis;         }     }     private static ConnectionMultiplexer GetManager(string connectionString = null)     {         if (string.IsNullOrEmpty(connectionString))         {             connectionString = GetDefaultConnectionString();         }         return ConnectionMultiplexer.Connect(connectionString);     }虽然ConnectionMultiplexer是实现了IDisposable接口的,但是我们基于重用的考虑,一般不需要去释放它。当作内存数据库使用IDatabase db = redis.GetDatabase(); 这里的GetDatabase() 返回的db对象是很轻量级别的,不需要被缓存起来,每次用每次拿即可。IDatabase 的所有方法都有同步和异步的实现。其中的异步实现都是可以await的。一些基础的操作的封装。public bool Remove(string key)     {         key = MergeKey(key);         var db = RedisManager.Manager.GetDatabase(Database);         return db.KeyDelete(key);     }     public string Get(string key)     {         key = this.MergeKey(key);         var db = RedisManager.Manager.GetDatabase(Database);         return db.StringGet(key);     }     public bool Set(string key, string value, int expireMinutes = 0)    
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

香煎三文鱼

你的鼓励将是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值