Skip to content

Commit

Permalink
修正RedisCache分布式缓存设置前缀无效的BUG,并添加单元测试
Browse files Browse the repository at this point in the history
  • Loading branch information
andywu188 committed Jul 8, 2024
1 parent f42f1ff commit 2b2b5ea
Show file tree
Hide file tree
Showing 3 changed files with 200 additions and 51 deletions.
62 changes: 14 additions & 48 deletions NewLife.Redis.Extensions/RedisCache.cs
Original file line number Diff line number Diff line change
@@ -1,82 +1,48 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using NewLife.Caching;
using NewLife.Log;

namespace NewLife.Redis.Extensions;

/// <summary>
/// Redis分布式缓存
/// </summary>
public class RedisCache : IDistributedCache, IDisposable
public class RedisCache : FullRedis, IDistributedCache, IDisposable
{
#region 属性
/// <summary>
/// Redis对象。可使用完整Redis功能
/// </summary>
public FullRedis Redis => _redis;

/// <summary>刷新时的过期时间。默认24小时</summary>
public TimeSpan Expire { get; set; } = TimeSpan.FromHours(24);

private readonly RedisOptions _options;
private readonly FullRedis _redis;
public new TimeSpan Expire { get; set; } = TimeSpan.FromHours(24);
#endregion

#region 构造
/// <summary>
/// 实例化Redis分布式缓存
/// </summary>
/// <param name="optionsAccessor"></param>
/// <param name="serviceProvider"></param>
/// <param name="optionsAccessor"></param>
/// <exception cref="ArgumentNullException"></exception>
public RedisCache(IOptions<RedisOptions> optionsAccessor, IServiceProvider serviceProvider)
public RedisCache(IServiceProvider serviceProvider, IOptions<RedisOptions> optionsAccessor) : base(serviceProvider, optionsAccessor.Value)
{
if (optionsAccessor == null) throw new ArgumentNullException(nameof(optionsAccessor));

_options = optionsAccessor.Value;

//_redis = new FullRedis
//{
// Name = _options.InstanceName,
// Tracer = serviceProvider.GetService<ITracer>(),
//};
_redis = _options.Prefix.IsNullOrEmpty() ? new FullRedis() : new PrefixedRedis();
_redis.Name = _options.InstanceName;
_redis.Tracer = serviceProvider.GetService<ITracer>();

if (!_options.Configuration.IsNullOrEmpty())
_redis.Init(_options.Configuration);
else
{
_redis.Server = _options.Server;
_redis.Db = _options.Db;
_redis.Password = _options.Password;
_redis.Timeout = _options.Timeout;
}
}

/// <summary>
/// 销毁
/// </summary>
public void Dispose() => _redis?.Dispose();
#endregion

/// <summary>
/// 获取
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public Byte[]? Get(String key) => _redis.Get<Byte[]>(key);
public Byte[]? Get(String key) => base.Get<Byte[]?>(key);

/// <summary>
/// 异步获取
/// </summary>
/// <param name="key"></param>
/// <param name="token"></param>
/// <returns></returns>
public Task<Byte[]?> GetAsync(String key, CancellationToken token = default) => Task.Run(() => _redis.Get<Byte[]>(key), token);
public Task<Byte[]?> GetAsync(String key, CancellationToken token = default) => Task.Run(() => base.Get<Byte[]>(key), token);

/// <summary>
/// 设置
Expand All @@ -91,16 +57,16 @@ public void Set(String key, Byte[] value, DistributedCacheEntryOptions options)
if (value == null) throw new ArgumentNullException(nameof(value));

if (options == null)
_redis.Set(key, value);
base.Set(key, value);
else
if (options.AbsoluteExpiration != null)
_redis.Set(key, value, options.AbsoluteExpiration.Value - DateTime.Now);
base.Set(key, value, options.AbsoluteExpiration.Value - DateTime.Now);
else if (options.AbsoluteExpirationRelativeToNow != null)
_redis.Set(key, value, options.AbsoluteExpirationRelativeToNow.Value);
base.Set(key, value, options.AbsoluteExpirationRelativeToNow.Value);
else if (options.SlidingExpiration != null)
_redis.Set(key, value, options.SlidingExpiration.Value);
base.Set(key, value, options.SlidingExpiration.Value);
else
_redis.Set(key, value);
base.Set(key, value);
}

/// <summary>
Expand All @@ -118,7 +84,7 @@ public void Set(String key, Byte[] value, DistributedCacheEntryOptions options)
/// </summary>
/// <param name="key"></param>
/// <exception cref="ArgumentNullException"></exception>
public void Refresh(String key) => _redis.SetExpire(key, Expire);
public void Refresh(String key) => base.SetExpire(key, Expire);

/// <summary>
/// 异步刷新
Expand All @@ -133,13 +99,13 @@ public void Set(String key, Byte[] value, DistributedCacheEntryOptions options)
/// 删除
/// </summary>
/// <param name="key"></param>
public void Remove(String key) => _redis.Remove(key);
public void Remove(String key) => base.Remove(key);

/// <summary>
/// 异步删除
/// </summary>
/// <param name="key"></param>
/// <param name="token"></param>
/// <returns></returns>
public Task RemoveAsync(String key, CancellationToken token = default) => Task.Run(() => _redis.Remove(key), token);
public Task RemoveAsync(String key, CancellationToken token = default) => Task.Run(() => base.Remove(key), token);
}
26 changes: 23 additions & 3 deletions NewLife.Redis.Extensions/RedisCacheServiceCollectionExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Options;
using NewLife.Caching;
using NewLife.Caching.Services;

namespace NewLife.Redis.Extensions;

Expand All @@ -10,7 +13,7 @@ namespace NewLife.Redis.Extensions;
public static class RedisCacheServiceCollectionExtensions
{
/// <summary>
/// 添加Redis分布式缓存
/// 添加Redis分布式缓存,应用内可使用RedisCache/FullRedis/Redis/IDistributedCache/ICache/ICacheProvider
/// </summary>
/// <param name="services"></param>
/// <param name="setupAction"></param>
Expand All @@ -23,9 +26,26 @@ public static IServiceCollection AddDistributedRedisCache(this IServiceCollectio
if (setupAction == null)
throw new ArgumentNullException(nameof(setupAction));

//services.AddOptions();

services.AddOptions();
services.Configure(setupAction);
services.Add(ServiceDescriptor.Singleton<IDistributedCache, RedisCache>());
services.AddSingleton(sp => new RedisCache(sp, sp.GetRequiredService<IOptions<RedisOptions>>()));
services.AddSingleton<IDistributedCache>(sp => sp.GetRequiredService<RedisCache>());

services.TryAddSingleton<FullRedis>(sp => sp.GetRequiredService<RedisCache>());
services.TryAddSingleton<ICache>(p => p.GetRequiredService<RedisCache>());
services.TryAddSingleton<Caching.Redis>(p => p.GetRequiredService<RedisCache>());

// 注册Redis缓存服务
services.TryAddSingleton(p =>
{
var redis = p.GetRequiredService<RedisCache>();
var provider = new RedisCacheProvider(p);
if (provider.Cache is not Caching.Redis) provider.Cache = redis;
provider.RedisQueue ??= redis;

return provider;
});

return services;
}
Expand Down
163 changes: 163 additions & 0 deletions XUnitTest/RedisCacheTests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.DependencyInjection;
using NewLife.Caching;
using NewLife.Redis.Extensions;
using Xunit;

namespace XUnitTest
{
public class RedisCacheTests
{
public readonly ServiceProvider provider;
private static readonly string prefix = "myPrefix:";
public RedisCacheTests()
{
var services = new ServiceCollection();
services.AddDistributedRedisCache(options =>
{
options.Server = "127.0.0.1";
options.Db = 9;
options.Prefix = prefix;
});
provider = services.BuildServiceProvider();
}

[Fact]
public void Get()
{
var key = "key1";
var value = Encoding.UTF8.GetBytes("value1");

var cache = provider.GetService<IDistributedCache>();
cache.Set(key, value, null);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

[Fact]
public async Task GetAsync()
{
var key = "key2";
var value = Encoding.UTF8.GetBytes("value2");

var cache = provider.GetService<IDistributedCache>();
await cache.SetAsync(key, value, null);

var rs = await cache.GetAsync(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

[Fact]
public void Set()
{
var key = "key3";
var value = Encoding.UTF8.GetBytes("value3");

var cache = provider.GetService<IDistributedCache>();
cache.Set(key, value, null);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

[Fact]
public async Task SetAsync()
{
var key = "key4";
var value = Encoding.UTF8.GetBytes("value4");

var cache = provider.GetService<IDistributedCache>();
await cache.SetAsync(key, value, null);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

[Fact]
public void Set_With_Options()
{
var key = "key5";
var value = Encoding.UTF8.GetBytes("value5");

var cache = provider.GetService<IDistributedCache>();
var options = new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTime.Now.AddSeconds(30)
};
cache.Set(key, value, options);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

[Fact]
public async Task SetAsync_With_Options()
{
var key = "key6";
var value = Encoding.UTF8.GetBytes("value6");

var cache = provider.GetService<IDistributedCache>();
var options = new DistributedCacheEntryOptions
{
AbsoluteExpiration = DateTime.Now.AddSeconds(30)
};
await cache.SetAsync(key, value, options);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

[Fact]
public void Set_With_Options_AbsoluteExpirationRelativeToNow()
{
var key = "key7";
var value = Encoding.UTF8.GetBytes("value7");

var cache = provider.GetService<IDistributedCache>();
var options = new DistributedCacheEntryOptions
{
AbsoluteExpirationRelativeToNow = TimeSpan.FromSeconds(30)
};
cache.Set(key, value, options);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);
}

/// <summary>
/// 测试缓存Key前缀
/// </summary>
[Fact]
public void PrefixTest()
{
var key = "key8";
var value = Encoding.UTF8.GetBytes("value8");

var cache = provider.GetService<IDistributedCache>();
cache.Set(key, value, null);

var rs = cache.Get(key);
Assert.NotNull(rs);
Assert.Equal(value, rs);

var cache2 = provider.GetService<Redis>();
var rs2 = cache.Get(prefix + key);
Assert.NotNull(rs2);
Assert.Equal(value, rs2);
}

}
}

0 comments on commit 2b2b5ea

Please sign in to comment.