44 changed files with 1073 additions and 1 deletions
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,24 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<None Remove="LINGYUN\Abp\CachingManagement\Localization\Resources\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<EmbeddedResource Include="LINGYUN\Abp\CachingManagement\Localization\Resources\*.json" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Authorization" Version="$(VoloAbpPackageVersion)" /> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application.Contracts" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,29 @@ |
|||
using LINGYUN.Abp.CachingManagement.Localization; |
|||
using Volo.Abp.Application; |
|||
using Volo.Abp.Authorization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
using Volo.Abp.VirtualFileSystem; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpAuthorizationModule), |
|||
typeof(AbpDddApplicationContractsModule))] |
|||
public class AbpCachingManagementApplicationContractsModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpVirtualFileSystemOptions>(options => |
|||
{ |
|||
options.FileSets.AddEmbedded<AbpCachingManagementApplicationContractsModule>(); |
|||
}); |
|||
|
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Add<CacheResource>() |
|||
.AddVirtualJson("/LINGYUN/Abp/CachingManagement/Localization/Resources"); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class AbpCachingManagementRemoteServiceConsts |
|||
{ |
|||
public const string RemoteServiceName = "CachingManagement"; |
|||
|
|||
public const string ModuleName = "caching-management"; |
|||
} |
|||
@ -0,0 +1,9 @@ |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class CacheKeyInput |
|||
{ |
|||
[Required] |
|||
public string Key { get; set; } |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class CacheKeysDto |
|||
{ |
|||
public string NextMarker { get; set; } |
|||
|
|||
public List<string> Keys { get; set; } = new List<string>(); |
|||
} |
|||
@ -0,0 +1,10 @@ |
|||
using System; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class CacheRefreshInput |
|||
{ |
|||
public string Key { get; set; } |
|||
public DateTime? AbsoluteExpiration { get; set; } |
|||
public DateTime? SlidingExpiration { get; set; } |
|||
} |
|||
@ -0,0 +1,12 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class CacheValueDto |
|||
{ |
|||
public string Type { get; set; } |
|||
public long Size { get; set; } |
|||
public DateTime? Expiration { get; set; } |
|||
public IDictionary<string, object> Values { get; set; } = new Dictionary<string, object>(); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class GetCacheKeysInput |
|||
{ |
|||
public string Prefix { get; set; } |
|||
public string Marker { get; set; } |
|||
public string Filter { get; set; } |
|||
} |
|||
@ -0,0 +1,15 @@ |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public interface ICacheAppService : IApplicationService |
|||
{ |
|||
Task<CacheKeysDto> GetKeysAsync(GetCacheKeysInput input); |
|||
|
|||
Task<CacheValueDto> GetValueAsync(CacheKeyInput input); |
|||
|
|||
Task RefreshAsync(CacheRefreshInput input); |
|||
|
|||
Task RemoveAsync(CacheKeyInput input); |
|||
} |
|||
@ -0,0 +1,8 @@ |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement.Localization; |
|||
|
|||
[LocalizationResourceName("CachingManagement")] |
|||
public class CacheResource |
|||
{ |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
{ |
|||
"culture": "en", |
|||
"texts": { |
|||
"Permission:CachingManagement": "Caching Management", |
|||
"Permission:Caches": "Manager Cache", |
|||
"Permission:Refresh": "Refresh", |
|||
"Permission:Delete": "Delete", |
|||
"Caches": "Caches", |
|||
"DisplayName:Marker": "Marker", |
|||
"DisplayName:NextMarker": "Next Marker", |
|||
"DisplayName:Type": "Type", |
|||
"DisplayName:Size": "Size", |
|||
"DisplayName:Ttl": "Ttl", |
|||
"DisplayName:Values": "Values", |
|||
"DisplayName:Key": "Key", |
|||
"DisplayName:Keys": "Keys", |
|||
"DisplayName:AbsoluteExpiration": "Absolute Expiration", |
|||
"DisplayName:SlidingExpiration": "Sliding Expiration", |
|||
"Abp.CachingManagement:01001": "A cache of Type {Type} does not support viewing key values." |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
{ |
|||
"culture": "zh-Hans", |
|||
"texts": { |
|||
"Permission:CachingManagement": "缓存管理", |
|||
"Permission:Caches": "管理缓存", |
|||
"Permission:Refresh": "刷新", |
|||
"Permission:Delete": "删除", |
|||
"Caches": "缓存列表", |
|||
"DisplayName:Marker": "标记", |
|||
"DisplayName:NextMarker": "下一个标记", |
|||
"DisplayName:Type": "类型", |
|||
"DisplayName:Size": "大小", |
|||
"DisplayName:Ttl": "存活时间", |
|||
"DisplayName:Values": "缓存值", |
|||
"DisplayName:Key": "缓存键", |
|||
"DisplayName:Keys": "键列表", |
|||
"DisplayName:AbsoluteExpiration": "绝对过期时间", |
|||
"DisplayName:SlidingExpiration": "滑动过期时间", |
|||
"Abp.CachingManagement:01001": "类型为 {Type} 的缓存暂不支持查看键值." |
|||
} |
|||
} |
|||
@ -0,0 +1,22 @@ |
|||
using LINGYUN.Abp.CachingManagement.Localization; |
|||
using Volo.Abp.Authorization.Permissions; |
|||
using Volo.Abp.Localization; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement.Permissions; |
|||
|
|||
public class CachingManagemenPermissionDefinitionProvider : PermissionDefinitionProvider |
|||
{ |
|||
public override void Define(IPermissionDefinitionContext context) |
|||
{ |
|||
var cachingManagerGroup = context.AddGroup(CachingManagementPermissionNames.GroupName, L("Permission:CachingManagement")); |
|||
|
|||
var cacheGroup = cachingManagerGroup.AddPermission(CachingManagementPermissionNames.Cache.Default, L("Permission:Caches")); |
|||
cacheGroup.AddChild(CachingManagementPermissionNames.Cache.Refresh, L("Permission:Refresh")); |
|||
cacheGroup.AddChild(CachingManagementPermissionNames.Cache.Delete, L("Permission:Delete")); |
|||
} |
|||
|
|||
private static LocalizableString L(string name) |
|||
{ |
|||
return LocalizableString.Create<CacheResource>(name); |
|||
} |
|||
} |
|||
@ -0,0 +1,20 @@ |
|||
using Volo.Abp.Reflection; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement.Permissions; |
|||
|
|||
public static class CachingManagementPermissionNames |
|||
{ |
|||
public const string GroupName = "AbpCachingManagement"; |
|||
|
|||
public static class Cache |
|||
{ |
|||
public const string Default = GroupName + ".Cache"; |
|||
public const string Refresh = Default + ".Refresh"; |
|||
public const string Delete = Default + ".Delete"; |
|||
} |
|||
|
|||
public static string[] GetAll() |
|||
{ |
|||
return ReflectionHelper.GetPublicConstantsRecursively(typeof(CachingManagementPermissionNames)); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,20 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Ddd.Application" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.CachingManagement.Application.Contracts\LINGYUN.Abp.CachingManagement.Application.Contracts.csproj" /> |
|||
<ProjectReference Include="..\LINGYUN.Abp.CachingManagement.Domain\LINGYUN.Abp.CachingManagement.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,12 @@ |
|||
using Volo.Abp.Application; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpCachingManagementApplicationContractsModule), |
|||
typeof(AbpDddApplicationModule))] |
|||
public class AbpCachingManagementApplicationModule : AbpModule |
|||
{ |
|||
|
|||
} |
|||
@ -0,0 +1,78 @@ |
|||
using LINGYUN.Abp.CachingManagement.Localization; |
|||
using LINGYUN.Abp.CachingManagement.Permissions; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using System; |
|||
using System.Linq; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp.Application.Services; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
[Authorize(CachingManagementPermissionNames.Cache.Default)] |
|||
public class CacheAppService : ApplicationService, ICacheAppService |
|||
{ |
|||
protected ICacheManager CacheManager { get; } |
|||
|
|||
public CacheAppService(ICacheManager cacheManager) |
|||
{ |
|||
CacheManager = cacheManager; |
|||
|
|||
LocalizationResource = typeof(CacheResource); |
|||
} |
|||
|
|||
public async virtual Task<CacheKeysDto> GetKeysAsync(GetCacheKeysInput input) |
|||
{ |
|||
var res = await CacheManager.GetKeysAsync( |
|||
input.Prefix, |
|||
input.Filter, |
|||
input.Marker); |
|||
|
|||
return new CacheKeysDto |
|||
{ |
|||
NextMarker = res.NextMarker, |
|||
Keys = res.Keys.ToList(), |
|||
}; |
|||
} |
|||
|
|||
public async virtual Task<CacheValueDto> GetValueAsync(CacheKeyInput input) |
|||
{ |
|||
var res = await CacheManager.GetValueAsync(input.Key); |
|||
|
|||
var value = new CacheValueDto |
|||
{ |
|||
Size = res.Size, |
|||
Type = res.Type, |
|||
Values = res.Values, |
|||
}; |
|||
if (res.Ttl.HasValue) |
|||
{ |
|||
value.Expiration = Clock.Now.Add(res.Ttl.Value); |
|||
} |
|||
|
|||
return value; |
|||
} |
|||
|
|||
[Authorize(CachingManagementPermissionNames.Cache.Refresh)] |
|||
public async virtual Task RefreshAsync(CacheRefreshInput input) |
|||
{ |
|||
TimeSpan? absExpir = null; |
|||
TimeSpan? sldExpr = null; |
|||
|
|||
if (input.AbsoluteExpiration.HasValue && input.AbsoluteExpiration.Value > Clock.Now) |
|||
{ |
|||
absExpir = input.AbsoluteExpiration.Value - Clock.Now; |
|||
} |
|||
if (input.SlidingExpiration.HasValue && input.SlidingExpiration.Value > Clock.Now) |
|||
{ |
|||
sldExpr = input.SlidingExpiration.Value - Clock.Now; |
|||
} |
|||
|
|||
await CacheManager.RefreshAsync(input.Key, absExpir, sldExpr); |
|||
} |
|||
|
|||
[Authorize(CachingManagementPermissionNames.Cache.Delete)] |
|||
public async virtual Task RemoveAsync(CacheKeyInput input) |
|||
{ |
|||
await CacheManager.RemoveAsync(input.Key); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,15 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Caching" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,17 @@ |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpCachingModule))] |
|||
public class AbpCachingManagementDomainModule : AbpModule |
|||
{ |
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpDistributedCacheOptions>(options => |
|||
{ |
|||
|
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,23 @@ |
|||
using System; |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class CacheValueResponse |
|||
{ |
|||
public string Type { get; } |
|||
public long Size { get; } |
|||
public TimeSpan? Ttl { get; } |
|||
public IDictionary<string, object> Values { get; } |
|||
public CacheValueResponse( |
|||
string type, |
|||
long size, |
|||
IDictionary<string, object> values, |
|||
TimeSpan? ttl = null) |
|||
{ |
|||
Type = type; |
|||
Size = size; |
|||
Ttl = ttl; |
|||
Values = values; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
using System.Collections.Generic; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class CackeKeysResponse |
|||
{ |
|||
public string NextMarker { get; } |
|||
|
|||
public IEnumerable<string> Keys { get; } |
|||
|
|||
public CackeKeysResponse( |
|||
string nextMarker, |
|||
IEnumerable<string> keys) |
|||
{ |
|||
NextMarker = nextMarker; |
|||
Keys = keys; |
|||
} |
|||
} |
|||
@ -0,0 +1,18 @@ |
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class GetCacheKeysRequest |
|||
{ |
|||
public string Prefix { get; } |
|||
public string Filter { get; } |
|||
public string Marker { get; } |
|||
|
|||
public GetCacheKeysRequest( |
|||
string prefix = null, |
|||
string filter = null, |
|||
string marker = null) |
|||
{ |
|||
Prefix = prefix; |
|||
Filter = filter; |
|||
Marker = marker; |
|||
} |
|||
} |
|||
@ -0,0 +1,16 @@ |
|||
using System.Collections.Generic; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public interface ICacheManager |
|||
{ |
|||
Task<CackeKeysResponse> GetKeysAsync(GetCacheKeysRequest request, CancellationToken cancellationToken = default); |
|||
|
|||
Task<CacheValueResponse> GetValueAsync(string key, CancellationToken cancellationToken = default); |
|||
|
|||
Task RefreshAsync(RefreshCacheRequest request, CancellationToken cancellationToken = default); |
|||
|
|||
Task RemoveAsync(string key, CancellationToken cancellationToken = default); |
|||
} |
|||
@ -0,0 +1,32 @@ |
|||
using System; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public static class ICacheManagerExtensions |
|||
{ |
|||
public static Task<CackeKeysResponse> GetKeysAsync( |
|||
this ICacheManager cacheManager, |
|||
string prefix = null, |
|||
string filter = null, |
|||
string marker = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return cacheManager.GetKeysAsync( |
|||
new GetCacheKeysRequest(prefix, filter, marker), |
|||
cancellationToken); |
|||
} |
|||
|
|||
public static Task RefreshAsync( |
|||
this ICacheManager cacheManager, |
|||
string key, |
|||
TimeSpan? absExpr = null, |
|||
TimeSpan? sldExpr = null, |
|||
CancellationToken cancellationToken = default) |
|||
{ |
|||
return cacheManager.RefreshAsync( |
|||
new RefreshCacheRequest(key, absExpr, sldExpr), |
|||
cancellationToken); |
|||
} |
|||
} |
|||
@ -0,0 +1,21 @@ |
|||
using System; |
|||
using System.ComponentModel.DataAnnotations; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
public class RefreshCacheRequest |
|||
{ |
|||
[Required] |
|||
public string Key { get; } |
|||
public TimeSpan? AbsoluteExpiration { get; } |
|||
public TimeSpan? SlidingExpiration { get; } |
|||
public RefreshCacheRequest( |
|||
string key, |
|||
TimeSpan? absoluteExpiration = null, |
|||
TimeSpan? slidingExpiration = null) |
|||
{ |
|||
Key = key; |
|||
AbsoluteExpiration = absoluteExpiration; |
|||
SlidingExpiration = slidingExpiration; |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>net6.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.AspNetCore.Mvc" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.CachingManagement.Application.Contracts\LINGYUN.Abp.CachingManagement.Application.Contracts.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,40 @@ |
|||
using LINGYUN.Abp.CachingManagement.Localization; |
|||
using Localization.Resources.AbpUi; |
|||
using Microsoft.Extensions.DependencyInjection; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
using Volo.Abp.AspNetCore.Mvc.Localization; |
|||
using Volo.Abp.Localization; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpCachingManagementApplicationContractsModule), |
|||
typeof(AbpAspNetCoreMvcModule))] |
|||
public class AbpCachingManagementHttpApiModule : AbpModule |
|||
{ |
|||
public override void PreConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
PreConfigure<IMvcBuilder>(mvcBuilder => |
|||
{ |
|||
mvcBuilder.AddApplicationPartIfNotExists(typeof(AbpCachingManagementHttpApiModule).Assembly); |
|||
}); |
|||
|
|||
PreConfigure<AbpMvcDataAnnotationsLocalizationOptions>(options => |
|||
{ |
|||
options.AddAssemblyResource( |
|||
typeof(CacheResource), |
|||
typeof(AbpCachingManagementApplicationContractsModule).Assembly); |
|||
}); |
|||
} |
|||
|
|||
public override void ConfigureServices(ServiceConfigurationContext context) |
|||
{ |
|||
Configure<AbpLocalizationOptions>(options => |
|||
{ |
|||
options.Resources |
|||
.Get<CacheResource>() |
|||
.AddBaseTypes(typeof(AbpUiResource)); |
|||
}); |
|||
} |
|||
} |
|||
@ -0,0 +1,56 @@ |
|||
using LINGYUN.Abp.CachingManagement.Localization; |
|||
using LINGYUN.Abp.CachingManagement.Permissions; |
|||
using Microsoft.AspNetCore.Authorization; |
|||
using Microsoft.AspNetCore.Mvc; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.AspNetCore.Mvc; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement; |
|||
|
|||
[Controller] |
|||
[Authorize(CachingManagementPermissionNames.Cache.Default)] |
|||
[RemoteService(Name = AbpCachingManagementRemoteServiceConsts.RemoteServiceName)] |
|||
[Area(AbpCachingManagementRemoteServiceConsts.ModuleName)] |
|||
[Route("api/caching-management/cache")] |
|||
public class CacheController : AbpControllerBase, ICacheAppService |
|||
{ |
|||
protected ICacheAppService CacheAppService { get; } |
|||
|
|||
public CacheController(ICacheAppService cacheAppService) |
|||
{ |
|||
CacheAppService = cacheAppService; |
|||
|
|||
LocalizationResource = typeof(CacheResource); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("keys")] |
|||
public virtual Task<CacheKeysDto> GetKeysAsync(GetCacheKeysInput input) |
|||
{ |
|||
return CacheAppService.GetKeysAsync(input); |
|||
} |
|||
|
|||
[HttpGet] |
|||
[Route("value")] |
|||
public virtual Task<CacheValueDto> GetValueAsync(CacheKeyInput input) |
|||
{ |
|||
return CacheAppService.GetValueAsync(input); |
|||
} |
|||
|
|||
[HttpPut] |
|||
[Route("refresh")] |
|||
[Authorize(CachingManagementPermissionNames.Cache.Refresh)] |
|||
public virtual Task RefreshAsync(CacheRefreshInput input) |
|||
{ |
|||
return CacheAppService.RefreshAsync(input); |
|||
} |
|||
|
|||
[HttpDelete] |
|||
[Route("remove")] |
|||
[Authorize(CachingManagementPermissionNames.Cache.Delete)] |
|||
public virtual Task RemoveAsync(CacheKeyInput input) |
|||
{ |
|||
return CacheAppService.RemoveAsync(input); |
|||
} |
|||
} |
|||
@ -0,0 +1,3 @@ |
|||
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd"> |
|||
<ConfigureAwait ContinueOnCapturedContext="false" /> |
|||
</Weavers> |
|||
@ -0,0 +1,30 @@ |
|||
<?xml version="1.0" encoding="utf-8"?> |
|||
<xs:schema xmlns:xs="http://www.w3.org/2001/XMLSchema"> |
|||
<!-- This file was generated by Fody. Manual changes to this file will be lost when your project is rebuilt. --> |
|||
<xs:element name="Weavers"> |
|||
<xs:complexType> |
|||
<xs:all> |
|||
<xs:element name="ConfigureAwait" minOccurs="0" maxOccurs="1"> |
|||
<xs:complexType> |
|||
<xs:attribute name="ContinueOnCapturedContext" type="xs:boolean" /> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:all> |
|||
<xs:attribute name="VerifyAssembly" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'true' to run assembly verification (PEVerify) on the target assembly after all weavers have been executed.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="VerifyIgnoreCodes" type="xs:string"> |
|||
<xs:annotation> |
|||
<xs:documentation>A comma-separated list of error codes that can be safely ignored in assembly verification.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
<xs:attribute name="GenerateXsd" type="xs:boolean"> |
|||
<xs:annotation> |
|||
<xs:documentation>'false' to turn off automatic generation of the XML Schema file.</xs:documentation> |
|||
</xs:annotation> |
|||
</xs:attribute> |
|||
</xs:complexType> |
|||
</xs:element> |
|||
</xs:schema> |
|||
@ -0,0 +1,19 @@ |
|||
<Project Sdk="Microsoft.NET.Sdk"> |
|||
|
|||
<Import Project="..\..\..\configureawait.props" /> |
|||
<Import Project="..\..\..\common.props" /> |
|||
|
|||
<PropertyGroup> |
|||
<TargetFramework>netstandard2.0</TargetFramework> |
|||
<RootNamespace /> |
|||
</PropertyGroup> |
|||
|
|||
<ItemGroup> |
|||
<PackageReference Include="Volo.Abp.Caching.StackExchangeRedis" Version="$(VoloAbpPackageVersion)" /> |
|||
</ItemGroup> |
|||
|
|||
<ItemGroup> |
|||
<ProjectReference Include="..\LINGYUN.Abp.CachingManagement.Domain\LINGYUN.Abp.CachingManagement.Domain.csproj" /> |
|||
</ItemGroup> |
|||
|
|||
</Project> |
|||
@ -0,0 +1,11 @@ |
|||
using Volo.Abp.Caching.StackExchangeRedis; |
|||
using Volo.Abp.Modularity; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement.StackExchangeRedis; |
|||
|
|||
[DependsOn( |
|||
typeof(AbpCachingManagementDomainModule), |
|||
typeof(AbpCachingStackExchangeRedisModule))] |
|||
public class AbpCachingManagementStackExchangeRedisModule : AbpModule |
|||
{ |
|||
} |
|||
@ -0,0 +1,228 @@ |
|||
using Microsoft.Extensions.Caching.Distributed; |
|||
using Microsoft.Extensions.Caching.StackExchangeRedis; |
|||
using Microsoft.Extensions.Options; |
|||
using StackExchange.Redis; |
|||
using System; |
|||
using System.Collections.Generic; |
|||
using System.Reflection; |
|||
using System.Threading; |
|||
using System.Threading.Tasks; |
|||
using Volo.Abp; |
|||
using Volo.Abp.Caching; |
|||
using Volo.Abp.Caching.StackExchangeRedis; |
|||
using Volo.Abp.DependencyInjection; |
|||
using Volo.Abp.MultiTenancy; |
|||
|
|||
namespace LINGYUN.Abp.CachingManagement.StackExchangeRedis; |
|||
|
|||
[Dependency(ReplaceServices = true)] |
|||
public class StackExchangeRedisCacheManager : ICacheManager, ISingletonDependency |
|||
{ |
|||
private readonly static MethodInfo GetRedisDatabaseMethod; |
|||
private readonly static MethodInfo ConnectAsyncMethod; |
|||
|
|||
protected RedisCacheOptions RedisCacheOptions { get; } |
|||
protected AbpDistributedCacheOptions CacheOptions { get; } |
|||
protected ICurrentTenant CurrentTenant { get; } |
|||
protected IDistributedCache DistributedCache { get; } |
|||
protected AbpRedisCache RedisCache => DistributedCache.As<AbpRedisCache>(); |
|||
|
|||
protected IDatabase RedisDatabase => GetRedisDatabase(); |
|||
private IDatabase _redisDatabase; |
|||
|
|||
static StackExchangeRedisCacheManager() |
|||
{ |
|||
var type = typeof(AbpRedisCache); |
|||
|
|||
ConnectAsyncMethod = type.GetMethod("ConnectAsync", BindingFlags.Instance | BindingFlags.NonPublic); |
|||
GetRedisDatabaseMethod = type.GetMethod("GetRedisDatabase", BindingFlags.Instance | BindingFlags.NonPublic); |
|||
} |
|||
|
|||
public StackExchangeRedisCacheManager( |
|||
ICurrentTenant currentTenant, |
|||
IDistributedCache distributedCache, |
|||
IOptions<AbpDistributedCacheOptions> cacheOptions, |
|||
IOptions<RedisCacheOptions> redisCacheOptions) |
|||
{ |
|||
CurrentTenant = currentTenant; |
|||
DistributedCache = distributedCache;// distributedCache.As<AbpRedisCache>();
|
|||
CacheOptions = cacheOptions.Value; |
|||
RedisCacheOptions = redisCacheOptions.Value; |
|||
} |
|||
|
|||
public async virtual Task<CackeKeysResponse> GetKeysAsync(GetCacheKeysRequest request, CancellationToken cancellationToken = default) |
|||
{ |
|||
await ConnectAsync(cancellationToken); |
|||
|
|||
// 缓存键名规则: InstanceName + (t + TenantId)(CurrentTenant.IsAvailable) + CacheItemName + KeyPrefix + Key
|
|||
// 缓存键名规则: InstanceName + (c:)(!CurrentTenant.IsAvailable) + CacheItemName + KeyPrefix + Key
|
|||
|
|||
var match = "*"; |
|||
// abp*
|
|||
if (!RedisCacheOptions.InstanceName.IsNullOrWhiteSpace()) |
|||
{ |
|||
match = RedisCacheOptions.InstanceName; |
|||
} |
|||
// abp*t:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx*
|
|||
// abp*c:*
|
|||
if (CurrentTenant.IsAvailable) |
|||
{ |
|||
match += "t:" + CurrentTenant.Id.ToString() + "*"; |
|||
} |
|||
else |
|||
{ |
|||
match += "c:*"; |
|||
} |
|||
// abp*t:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx*application*
|
|||
// abp*c:*application*
|
|||
if (!CacheOptions.KeyPrefix.IsNullOrWhiteSpace()) |
|||
{ |
|||
match += CacheOptions.KeyPrefix + "*"; |
|||
} |
|||
|
|||
// app*abp*t:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx*application*
|
|||
// app*abp*c:*application*
|
|||
if (!request.Prefix.IsNullOrWhiteSpace()) |
|||
{ |
|||
match = request.Prefix + "*" + match; |
|||
} |
|||
|
|||
// if filter is Mailing:
|
|||
// app*abp*t:xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx*application*Mailing*
|
|||
// app*abp*c:*application*Mailing*
|
|||
if (!request.Filter.IsNullOrWhiteSpace()) |
|||
{ |
|||
match += request.Filter + "*"; |
|||
} |
|||
// scan 0 match * count 50000
|
|||
// redis有自定义的key排序,由传递的marker来确定下一次检索起始位
|
|||
|
|||
var args = new object[] { request.Marker ?? "0", "match", match, "count", 50000 }; |
|||
|
|||
var result = await RedisDatabase.ExecuteAsync("scan", args); |
|||
|
|||
var results = (RedisResult[])result; |
|||
|
|||
// 第一个返回结果 下一次检索起始位 0复位
|
|||
// 第二个返回结果为key列表
|
|||
// https://redis.io/commands/scan/
|
|||
return new CackeKeysResponse( |
|||
(string)results[0], |
|||
(string[])results[1]); |
|||
} |
|||
|
|||
public async virtual Task<CacheValueResponse> GetValueAsync(string key, CancellationToken cancellationToken = default) |
|||
{ |
|||
await ConnectAsync(cancellationToken); |
|||
|
|||
long size = 0; |
|||
var values = new Dictionary<string, object>(); |
|||
|
|||
// type RedisKey
|
|||
var type = await RedisDatabase.KeyTypeAsync(key); |
|||
// ttl RedisKey
|
|||
var ttl = await RedisDatabase.KeyTimeToLiveAsync(key); |
|||
|
|||
switch (type) |
|||
{ |
|||
case RedisType.Hash: |
|||
// hlen RedisKey
|
|||
size = await RedisDatabase.HashLengthAsync(key); |
|||
// hscan RedisKey
|
|||
var hvalues = RedisDatabase.HashScan(key); |
|||
foreach (var hvalue in hvalues) |
|||
{ |
|||
if (!hvalue.Name.IsNullOrEmpty) |
|||
{ |
|||
values.Add(hvalue.Name.ToString(), hvalue.Value.IsNullOrEmpty ? "" : hvalue.Value.ToString()); |
|||
} |
|||
} |
|||
break; |
|||
case RedisType.String: |
|||
// strlen RedisKey
|
|||
size = await RedisDatabase.StringLengthAsync(key); |
|||
// get RedisKey
|
|||
var svalue = RedisDatabase.StringGet(key); |
|||
values.Add("value", svalue.IsNullOrEmpty ? "" : svalue.ToString()); |
|||
break; |
|||
case RedisType.List: |
|||
// llen RedisKey
|
|||
size = await RedisDatabase.ListLengthAsync(key); |
|||
// lrange RedisKey
|
|||
var lvalues = RedisDatabase.ListRange(key); |
|||
for (var lindex = 0; lindex < lvalues.Length; lindex++) |
|||
{ |
|||
if (!lvalues[lindex].IsNullOrEmpty) |
|||
{ |
|||
values.Add($"index.{lindex}", lvalues[lindex].IsNullOrEmpty ? "" : lvalues[lindex].ToString()); |
|||
} |
|||
} |
|||
break; |
|||
default: |
|||
throw new BusinessException("Abp.CachingManagement:01001") |
|||
.WithData("Type", type.ToString()); |
|||
} |
|||
|
|||
return new CacheValueResponse( |
|||
type.ToString(), |
|||
size, |
|||
values, |
|||
ttl); |
|||
} |
|||
|
|||
public async virtual Task RefreshAsync(RefreshCacheRequest request, CancellationToken cancellationToken = default) |
|||
{ |
|||
var cacheKey = request.Key; |
|||
if (!RedisCacheOptions.InstanceName.IsNullOrWhiteSpace() && cacheKey.StartsWith(RedisCacheOptions.InstanceName)) |
|||
{ |
|||
cacheKey = cacheKey.Substring(RedisCacheOptions.InstanceName.Length); |
|||
} |
|||
if (request.AbsoluteExpiration.HasValue || request.SlidingExpiration.HasValue) |
|||
{ |
|||
var value = await RedisCache.GetAsync(cacheKey, cancellationToken); |
|||
|
|||
await RedisCache.SetAsync( |
|||
cacheKey, |
|||
value, |
|||
new DistributedCacheEntryOptions |
|||
{ |
|||
SlidingExpiration = request.SlidingExpiration, |
|||
AbsoluteExpirationRelativeToNow = request.AbsoluteExpiration, |
|||
}, |
|||
cancellationToken); |
|||
|
|||
return; |
|||
} |
|||
await RedisCache.RefreshAsync(cacheKey, cancellationToken); |
|||
} |
|||
|
|||
public async virtual Task RemoveAsync(string key, CancellationToken cancellationToken = default) |
|||
{ |
|||
var cacheKey = key; |
|||
if (!RedisCacheOptions.InstanceName.IsNullOrWhiteSpace() && cacheKey.StartsWith(RedisCacheOptions.InstanceName)) |
|||
{ |
|||
cacheKey = cacheKey.Substring(RedisCacheOptions.InstanceName.Length); |
|||
} |
|||
await RedisCache.RemoveAsync(cacheKey, cancellationToken); |
|||
} |
|||
|
|||
protected virtual Task ConnectAsync(CancellationToken token = default) |
|||
{ |
|||
if (_redisDatabase != null) |
|||
{ |
|||
return Task.CompletedTask; |
|||
} |
|||
|
|||
return (Task)ConnectAsyncMethod.Invoke(RedisCache, new object[] { token }); |
|||
} |
|||
|
|||
private IDatabase GetRedisDatabase() |
|||
{ |
|||
if (_redisDatabase == null) |
|||
{ |
|||
_redisDatabase = GetRedisDatabaseMethod.Invoke(RedisCache, null) as IDatabase; |
|||
} |
|||
|
|||
return _redisDatabase; |
|||
} |
|||
} |
|||
Loading…
Reference in new issue