Browse Source

Update `AbpRedisCache` to compatible net 9.

pull/20803/head
maliming 2 years ago
parent
commit
ea7383ad35
  1. 3
      framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs
  2. 183
      framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs
  3. 4
      framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs
  4. 2
      framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs
  5. 2
      framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs

3
framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpCachingStackExchangeRedisModule.cs

@ -27,8 +27,7 @@ public class AbpCachingStackExchangeRedisModule : AbpModule
}
});
//liangshiwei will update the AbpRedisCache
//context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache, AbpRedisCache>());
context.Services.Replace(ServiceDescriptor.Singleton<IDistributedCache, AbpRedisCache>());
}
}
}

183
framework/src/Volo.Abp.Caching.StackExchangeRedis/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache.cs

@ -1,4 +1,5 @@
using System;
using System.Buffers;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
@ -16,21 +17,21 @@ namespace Volo.Abp.Caching.StackExchangeRedis;
[DisableConventionalRegistration]
public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
{
protected static readonly string AbsoluteExpirationKey;
protected static readonly string SlidingExpirationKey;
protected static readonly string DataKey;
protected static readonly long NotPresent;
protected static readonly RedisValue[] HashMembersAbsoluteExpirationSlidingExpirationData;
protected static readonly RedisValue[] HashMembersAbsoluteExpirationSlidingExpiration;
private readonly static FieldInfo SetScriptField;
private readonly static FieldInfo RedisDatabaseField;
private readonly static MethodInfo ConnectMethod;
private readonly static MethodInfo ConnectAsyncMethod;
private readonly static MethodInfo MapMetadataMethod;
private readonly static MethodInfo GetAbsoluteExpirationMethod;
private readonly static MethodInfo GetExpirationInSecondsMethod;
private readonly static MethodInfo OnRedisErrorMethod;
protected readonly static string AbsoluteExpirationKey;
protected readonly static string SlidingExpirationKey;
protected readonly static string DataKey;
protected readonly static long NotPresent;
protected readonly static RedisValue[] HashMembersAbsoluteExpirationSlidingExpirationData;
protected readonly static RedisValue[] HashMembersAbsoluteExpirationSlidingExpiration;
protected readonly static FieldInfo RedisDatabaseField;
protected readonly static MethodInfo ConnectMethod;
protected readonly static MethodInfo ConnectAsyncMethod;
protected readonly static MethodInfo MapMetadataMethod;
protected readonly static MethodInfo GetAbsoluteExpirationMethod;
protected readonly static MethodInfo GetExpirationInSecondsMethod;
protected readonly static MethodInfo OnRedisErrorMethod;
protected readonly static MethodInfo RecycleMethodInfo;
protected RedisKey InstancePrefix { get; }
@ -40,8 +41,6 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
RedisDatabaseField = Check.NotNull(type.GetField("_cache", BindingFlags.Instance | BindingFlags.NonPublic), nameof(RedisDatabaseField));
SetScriptField = Check.NotNull(type.GetField("_setScript", BindingFlags.Instance | BindingFlags.NonPublic), nameof(SetScriptField));
ConnectMethod = Check.NotNull(type.GetMethod("Connect", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectMethod));
ConnectAsyncMethod = Check.NotNull(type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic), nameof(ConnectAsyncMethod));
@ -51,9 +50,11 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
GetAbsoluteExpirationMethod = Check.NotNull(type.GetMethod("GetAbsoluteExpiration", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetAbsoluteExpirationMethod));
GetExpirationInSecondsMethod = Check.NotNull(type.GetMethod("GetExpirationInSeconds", BindingFlags.Static | BindingFlags.NonPublic), nameof(GetExpirationInSecondsMethod));
OnRedisErrorMethod = Check.NotNull(type.GetMethod("OnRedisError", BindingFlags.Instance | BindingFlags.NonPublic), nameof(OnRedisErrorMethod));
RecycleMethodInfo = Check.NotNull(type.GetMethod("Recycle", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.Static), nameof(RecycleMethodInfo));
AbsoluteExpirationKey = type.GetField("AbsoluteExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!;
SlidingExpirationKey = type.GetField("SlidingExpirationKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!;
@ -61,9 +62,9 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
DataKey = type.GetField("DataKey", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.ToString()!;
NotPresent = type.GetField("NotPresent", BindingFlags.Static | BindingFlags.NonPublic)!.GetValue(null)!.To<int>();
HashMembersAbsoluteExpirationSlidingExpirationData = [AbsoluteExpirationKey, SlidingExpirationKey, DataKey];
HashMembersAbsoluteExpirationSlidingExpiration = [AbsoluteExpirationKey, SlidingExpirationKey];
}
@ -78,7 +79,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
}
protected virtual IDatabase Connect()
{
{
return (IDatabase)ConnectMethod.Invoke(this, Array.Empty<object>())!;
}
@ -87,32 +88,36 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return await (ValueTask<IDatabase>)ConnectAsyncMethod.Invoke(this, new object[] { token })!;
}
public byte[]?[] GetMany(
IEnumerable<string> keys)
protected virtual void Recycle(byte[]? lease)
{
RecycleMethodInfo.Invoke(this, new object[] { lease! });
}
public byte[]?[] GetMany(IEnumerable<string> keys)
{
keys = Check.NotNull(keys, nameof(keys));
return GetAndRefreshMany(keys, true);
}
public async Task<byte[]?[]> GetManyAsync(
IEnumerable<string> keys,
CancellationToken token = default)
public async Task<byte[]?[]> GetManyAsync(IEnumerable<string> keys, CancellationToken token = default)
{
keys = Check.NotNull(keys, nameof(keys));
return await GetAndRefreshManyAsync(keys, true, token);
}
public void SetMany(
IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options)
public void SetMany(IEnumerable<KeyValuePair<string, byte[]>> items, DistributedCacheEntryOptions options)
{
var cache = Connect();
try
{
Task.WaitAll(PipelineSetMany(cache, items, options));
Task.WaitAll(PipelineSetMany(cache, items, options, out var leases));
foreach (var lease in leases)
{
Recycle(lease);
}
}
catch (Exception ex)
{
@ -121,10 +126,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
}
}
public async Task SetManyAsync(
IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options,
CancellationToken token = default)
public async Task SetManyAsync( IEnumerable<KeyValuePair<string, byte[]>> items, DistributedCacheEntryOptions options, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
@ -132,7 +134,11 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
try
{
await Task.WhenAll(PipelineSetMany(cache, items, options));
await Task.WhenAll(PipelineSetMany(cache, items, options, out var leases));
foreach (var lease in leases)
{
Recycle(lease);
}
}
catch (Exception ex)
{
@ -141,17 +147,14 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
}
}
public void RefreshMany(
IEnumerable<string> keys)
public void RefreshMany(IEnumerable<string> keys)
{
keys = Check.NotNull(keys, nameof(keys));
GetAndRefreshMany(keys, false);
}
public async Task RefreshManyAsync(
IEnumerable<string> keys,
CancellationToken token = default)
public async Task RefreshManyAsync(IEnumerable<string> keys, CancellationToken token = default)
{
keys = Check.NotNull(keys, nameof(keys));
@ -193,9 +196,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
}
}
protected virtual byte[]?[] GetAndRefreshMany(
IEnumerable<string> keys,
bool getData)
protected virtual byte[]?[] GetAndRefreshMany(IEnumerable<string> keys, bool getData)
{
var cache = Connect();
@ -217,10 +218,7 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return bytes;
}
protected virtual async Task<byte[]?[]> GetAndRefreshManyAsync(
IEnumerable<string> keys,
bool getData,
CancellationToken token = default)
protected virtual async Task<byte[]?[]> GetAndRefreshManyAsync(IEnumerable<string> keys, bool getData, CancellationToken token = default)
{
token.ThrowIfCancellationRequested();
@ -239,15 +237,11 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
OnRedisError(ex, cache);
throw;
}
return bytes;
}
protected virtual Task[] PipelineRefreshManyAndOutData(
IDatabase cache,
RedisKey[] keys,
RedisValue[][] results,
out byte[]?[] bytes)
protected virtual Task[] PipelineRefreshManyAndOutData(IDatabase cache, RedisKey[] keys, RedisValue[][] results, out byte[]?[] bytes)
{
bytes = new byte[keys.Length][];
var tasks = new Task[keys.Length];
@ -293,37 +287,36 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
return tasks;
}
protected virtual Task[] PipelineSetMany(
IDatabase cache,
IEnumerable<KeyValuePair<string, byte[]>> items,
DistributedCacheEntryOptions options)
protected virtual Task[] PipelineSetMany(IDatabase cache, IEnumerable<KeyValuePair<string, byte[]>> items, DistributedCacheEntryOptions options, out List<byte[]?> leases)
{
items = Check.NotNull(items, nameof(items));
options = Check.NotNull(options, nameof(options));
var tasks = new List<Task>();
leases = new List<byte[]?>();
var itemArray = items.ToArray();
var tasks = new Task[itemArray.Length];
var creationTime = DateTimeOffset.UtcNow;
var absoluteExpiration = GetAbsoluteExpiration(creationTime, options);
for (var i = 0; i < itemArray.Length; i++)
foreach (var item in items)
{
tasks[i] = cache.ScriptEvaluateAsync(GetSetScript(), new RedisKey[] { InstancePrefix.Append(itemArray[i].Key) },
[
absoluteExpiration?.Ticks ?? NotPresent,
options.SlidingExpiration?.Ticks ?? NotPresent,
GetExpirationInSeconds(creationTime, absoluteExpiration, options) ?? NotPresent,
itemArray[i].Value
]);
var prefixedKey = InstancePrefix.Append(item.Key);
var ttl = GetExpirationInSeconds(creationTime, absoluteExpiration, options);
var fields = GetHashFields(Linearize(new ReadOnlySequence<byte>(item.Value), out var lease), absoluteExpiration, options.SlidingExpiration);
leases.Add(lease);
if (ttl is null)
{
tasks.Add(cache.HashSetAsync(prefixedKey, fields));
}
else
{
tasks.Add(cache.HashSetAsync(prefixedKey, fields));
tasks.Add( cache.KeyExpireAsync(prefixedKey, TimeSpan.FromSeconds(ttl.GetValueOrDefault())));
}
}
return tasks;
return tasks.ToArray();
}
protected virtual void MapMetadata(
RedisValue[] results,
out DateTimeOffset? absoluteExpiration,
out TimeSpan? slidingExpiration)
protected virtual void MapMetadata(RedisValue[] results, out DateTimeOffset? absoluteExpiration, out TimeSpan? slidingExpiration)
{
var parameters = new object?[] { results, null, null };
MapMetadataMethod.Invoke(this, parameters);
@ -332,36 +325,50 @@ public class AbpRedisCache : RedisCache, ICacheSupportsMultipleItems
slidingExpiration = (TimeSpan?)parameters[2];
}
protected virtual long? GetExpirationInSeconds(
DateTimeOffset creationTime,
DateTimeOffset? absoluteExpiration,
DistributedCacheEntryOptions options)
protected virtual long? GetExpirationInSeconds(DateTimeOffset creationTime, DateTimeOffset? absoluteExpiration, DistributedCacheEntryOptions options)
{
return (long?)GetExpirationInSecondsMethod.Invoke(null,
new object?[] { creationTime, absoluteExpiration, options });
return (long?)GetExpirationInSecondsMethod.Invoke(null, new object?[] { creationTime, absoluteExpiration, options });
}
protected virtual DateTimeOffset? GetAbsoluteExpiration(
DateTimeOffset creationTime,
DistributedCacheEntryOptions options)
protected virtual DateTimeOffset? GetAbsoluteExpiration(DateTimeOffset creationTime, DistributedCacheEntryOptions options)
{
return (DateTimeOffset?)GetAbsoluteExpirationMethod.Invoke(null, new object[] { creationTime, options });
}
protected virtual void OnRedisError(Exception ex, IDatabase cache)
{
OnRedisErrorMethod.Invoke(this, [ex, cache]);
}
private string GetSetScript()
private static ReadOnlyMemory<byte> Linearize(in ReadOnlySequence<byte> value, out byte[]? lease)
{
return SetScriptField.GetValue(this)!.ToString()!;
// RedisValue only supports single-segment chunks; this will almost never be an issue, but
// on those rare occasions: use a leased array to harmonize things
if (value.IsSingleSegment)
{
lease = null;
return value.First;
}
var length = checked((int)value.Length);
lease = ArrayPool<byte>.Shared.Rent(length);
value.CopyTo(lease);
return new(lease, 0, length);
}
private static RedisValue[] GetHashFields(bool getData)
{
return getData
? HashMembersAbsoluteExpirationSlidingExpirationData
: HashMembersAbsoluteExpirationSlidingExpiration;
}
private static HashEntry[] GetHashFields(RedisValue value, DateTimeOffset? absoluteExpiration, TimeSpan? slidingExpiration)
{
return
[
new HashEntry(AbsoluteExpirationKey, absoluteExpiration?.Ticks ?? NotPresent),
new HashEntry(SlidingExpirationKey, slidingExpiration?.Ticks ?? NotPresent),
new HashEntry(DataKey, value)
];
}
}

4
framework/src/Volo.Abp.EntityFrameworkCore/Volo/Abp/EntityFrameworkCore/AbpDbContext.cs

@ -114,7 +114,6 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
{
//TODO: Re-check.
optionsBuilder.ConfigureWarnings(c => c.Ignore(RelationalEventId.PendingModelChangesWarning));
base.OnConfiguring(optionsBuilder);
}
@ -628,7 +627,7 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
originalExtraProperties = entry.OriginalValues.GetValue<ExtraPropertyDictionary>(nameof(IHasExtraProperties.ExtraProperties));
}
//TODO: Reload will throw an exception. Check it later.
//TODO: Reload will throw an exception. Check it when new EF Core versions released.
//entry.Reload();
var storeValues = entry.OriginalValues;
@ -636,7 +635,6 @@ public abstract class AbpDbContext<TDbContext> : DbContext, IAbpEfCoreDbContext,
entry.OriginalValues.SetValues(storeValues);
entry.State = EntityState.Unchanged;
if (entry.Entity is IHasExtraProperties)
{
ObjectHelper.TrySetProperty(entry.Entity.As<IHasExtraProperties>(), x => x.ExtraProperties, () => originalExtraProperties);

2
framework/test/Volo.Abp.Caching.StackExchangeRedis.Tests/Volo/Abp/Caching/StackExchangeRedis/AbpRedisCache_Tests.cs

@ -13,7 +13,7 @@ public class AbpRedisCache_Tests : AbpCachingStackExchangeRedisTestBase
_distributedCache = GetRequiredService<IDistributedCache>();
}
[Fact(Skip = "liangshiwei will update the AbpRedisCache")]
[Fact]
public void Should_Replace_RedisCache()
{
(_distributedCache is AbpRedisCache).ShouldBeTrue();

2
framework/test/Volo.Abp.EntityFrameworkCore.Tests/Volo/Abp/EntityFrameworkCore/AbpEntityFrameworkCoreTestModule.cs

@ -79,7 +79,7 @@ public class AbpEntityFrameworkCoreTestModule : AbpModule
public override void OnPreApplicationInitialization(ApplicationInitializationContext context)
{
//context.ServiceProvider.GetRequiredService<SecondDbContext>().Database.Migrate();
context.ServiceProvider.GetRequiredService<SecondDbContext>().Database.Migrate();
using (var scope = context.ServiceProvider.CreateScope())
{
var categoryRepository = scope.ServiceProvider.GetRequiredService<IBasicRepository<Category, Guid>>();

Loading…
Cancel
Save