Commit d94c8ddc authored by Super's avatar Super

Main features completed.

parent 4d8a5451
......@@ -49,6 +49,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.CacheManagement.Hos
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyAbp.CacheManagement.Web.Unified", "host\EasyAbp.CacheManagement.Web.Unified\EasyAbp.CacheManagement.Web.Unified.csproj", "{3D872C41-E226-45C8-89C1-9D3DBD7C73F2}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "driver", "driver", "{7B138737-D818-4F2C-8A56-6264CF62B5B6}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyAbp.CacheManagement.StackExchangeRedis", "driver\EasyAbp.CacheManagement.StackExchangeRedis\EasyAbp.CacheManagement.StackExchangeRedis.csproj", "{32D77380-7200-4C6D-B2D7-D0CEC1BBD0B2}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
......@@ -135,6 +139,10 @@ Global
{3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{3D872C41-E226-45C8-89C1-9D3DBD7C73F2}.Release|Any CPU.Build.0 = Release|Any CPU
{32D77380-7200-4C6D-B2D7-D0CEC1BBD0B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{32D77380-7200-4C6D-B2D7-D0CEC1BBD0B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{32D77380-7200-4C6D-B2D7-D0CEC1BBD0B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{32D77380-7200-4C6D-B2D7-D0CEC1BBD0B2}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
......@@ -160,6 +168,7 @@ Global
{690203F4-3CD5-4569-88D9-EE831EEA5F5F} = {E400416D-2895-4512-9D17-90681EEC7E0A}
{F6AC8D4A-EDD7-4514-8E8A-5BCB019864DB} = {E400416D-2895-4512-9D17-90681EEC7E0A}
{3D872C41-E226-45C8-89C1-9D3DBD7C73F2} = {E400416D-2895-4512-9D17-90681EEC7E0A}
{32D77380-7200-4C6D-B2D7-D0CEC1BBD0B2} = {7B138737-D818-4F2C-8A56-6264CF62B5B6}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {4324B3B4-B60B-4E3C-91D8-59576B4E26DD}
......
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
<RootNamespace />
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\EasyAbp.CacheManagement.Domain\EasyAbp.CacheManagement.Domain.csproj" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.0" />
</ItemGroup>
</Project>
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Microsoft.Extensions.Caching.StackExchangeRedis;
using Microsoft.Extensions.Configuration;
using StackExchange.Redis;
using Volo.Abp.Caching;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Domain.Services;
using Volo.Abp.Threading;
namespace EasyAbp.CacheManagement.CacheItems
{
[Dependency(ReplaceServices = true)]
public class StackExchangeRedisCacheItemManager : DomainService, ICacheItemManager
{
private readonly IDistributedCache _distributedCache;
private readonly IDistributedCacheKeyNormalizer _keyNormalizer;
private readonly ICancellationTokenProvider _cancellationTokenProvider;
private readonly ConnectionMultiplexer _connectionMultiplexer;
public StackExchangeRedisCacheItemManager(
IDistributedCache distributedCache,
IDistributedCacheKeyNormalizer keyNormalizer,
ICancellationTokenProvider cancellationTokenProvider,
IConfiguration configuration)
{
if (!(distributedCache is RedisCache))
{
throw new DistributedCacheProviderInvalidException();
}
_distributedCache = distributedCache;
_keyNormalizer = keyNormalizer;
_cancellationTokenProvider = cancellationTokenProvider;
_connectionMultiplexer = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
}
public async Task<IEnumerable<string>> GetKeysAsync(CacheItem cacheItem,
CancellationToken cancellationToken = default)
{
var normalizedKey = GetNormalizedKey(cacheItem, "*");
var endPoint = _connectionMultiplexer.GetEndPoints().First();
var redisKeys = _connectionMultiplexer.GetServer(endPoint)
.Keys(_connectionMultiplexer.GetDatabase().Database, normalizedKey);
return redisKeys.Select(k => k.ToString());
}
public async Task ClearAllAsync(CancellationToken cancellationToken = default)
{
var endpoints = _connectionMultiplexer.GetEndPoints(true);
foreach (var endpoint in endpoints)
{
var server = _connectionMultiplexer.GetServer(endpoint);
await server.FlushDatabaseAsync(_connectionMultiplexer.GetDatabase().Database);
}
}
public async Task ClearAsync(CacheItem cacheItem, CancellationToken cancellationToken = default)
{
var token = _cancellationTokenProvider.FallbackToProvider();
var keys = await GetKeysAsync(cacheItem, token);
foreach (var key in keys)
{
await _distributedCache.RemoveAsync(key, token);
}
}
public async Task ClearSpecificAsync(CacheItem cacheItem, string cacheKey,
CancellationToken cancellationToken = default)
{
var key = GetNormalizedKey(cacheItem, cacheKey);
await _distributedCache.RemoveAsync(key, _cancellationTokenProvider.FallbackToProvider());
}
public async Task<string> GetValueAsync(string cacheKey, CancellationToken cancellationToken = default)
{
return await _distributedCache.GetStringAsync(cacheKey, _cancellationTokenProvider.FallbackToProvider());
}
protected virtual string GetNormalizedKey(CacheItem cacheItem, string cacheKey)
{
return _keyNormalizer.NormalizeKey(new DistributedCacheKeyNormalizeArgs(cacheKey, cacheItem.CacheName,
cacheItem.IgnoreMultiTenancy));
}
}
}
\ No newline at end of file
using Volo.Abp.Modularity;
namespace EasyAbp.CacheManagement
{
[DependsOn(
typeof(CacheManagementDomainModule)
)]
public class CacheManagementStackExchangeRedisModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
}
}
}
<Weavers xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="FodyWeavers.xsd">
<ConfigureAwait />
</Weavers>
\ No newline at end of file
<?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>
\ No newline at end of file
......@@ -5,7 +5,10 @@ using Microsoft.Extensions.Hosting;
using EasyAbp.CacheManagement.EntityFrameworkCore;
using EasyAbp.CacheManagement.MultiTenancy;
using EasyAbp.CacheManagement.Web;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.AspNetCore.Hosting;
using Microsoft.OpenApi.Models;
using StackExchange.Redis;
using Swashbuckle.AspNetCore.Swagger;
using Volo.Abp;
using Volo.Abp.Account;
......@@ -59,7 +62,8 @@ namespace EasyAbp.CacheManagement
typeof(AbpTenantManagementApplicationModule),
typeof(AbpTenantManagementEntityFrameworkCoreModule),
typeof(AbpAspNetCoreMvcUiBasicThemeModule),
typeof(AbpAspNetCoreSerilogModule)
typeof(AbpAspNetCoreSerilogModule),
typeof(CacheManagementStackExchangeRedisModule)
)]
public class CacheManagementWebUnifiedModule : AbpModule
{
......@@ -108,6 +112,19 @@ namespace EasyAbp.CacheManagement
options.IsEnabled = MultiTenancyConsts.IsEnabled;
});
context.Services.AddStackExchangeRedisCache(options =>
{
options.Configuration = configuration["Redis:Configuration"];
});
if (!hostingEnvironment.IsDevelopment())
{
var redis = ConnectionMultiplexer.Connect(configuration["Redis:Configuration"]);
context.Services
.AddDataProtection()
.PersistKeysToStackExchangeRedis(redis, "CacheManagement-Protection-Keys");
}
ConfigureConventionalControllers();
}
......
<Project Sdk="Microsoft.NET.Sdk.Web">
<Project Sdk="Microsoft.NET.Sdk.Web">
<Import Project="..\..\common.props" />
......@@ -14,6 +14,8 @@
<PackageReference Include="Serilog.Sinks.File" Version="4.1.0" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="5.0.0" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Tools" Version="3.1.0" />
<PackageReference Include="Microsoft.Extensions.Caching.StackExchangeRedis" Version="3.1.0" />
<PackageReference Include="Microsoft.AspNetCore.DataProtection.StackExchangeRedis" Version="3.1.0" />
<PackageReference Include="Volo.Abp.Autofac" Version="2.3.0" />
<PackageReference Include="Volo.Abp.AspNetCore.Mvc.UI.Theme.Basic" Version="2.3.0" />
<PackageReference Include="Volo.Abp.EntityFrameworkCore.SqlServer" Version="2.3.0" />
......@@ -35,6 +37,7 @@
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\driver\EasyAbp.CacheManagement.StackExchangeRedis\EasyAbp.CacheManagement.StackExchangeRedis.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.CacheManagement.Application\EasyAbp.CacheManagement.Application.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.CacheManagement.EntityFrameworkCore\EasyAbp.CacheManagement.EntityFrameworkCore.csproj" />
<ProjectReference Include="..\..\src\EasyAbp.CacheManagement.Web\EasyAbp.CacheManagement.Web.csproj" />
......
using Microsoft.EntityFrameworkCore.Migrations;
namespace EasyAbp.CacheManagement.Migrations
{
public partial class ImprovedCacheItemEntity : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "FullTypeName",
table: "CacheManagementCacheItems");
migrationBuilder.AlterColumn<string>(
name: "DisplayName",
table: "CacheManagementCacheItems",
nullable: false,
oldClrType: typeof(string),
oldType: "nvarchar(max)",
oldNullable: true);
migrationBuilder.AddColumn<string>(
name: "CacheName",
table: "CacheManagementCacheItems",
nullable: false,
defaultValue: "");
migrationBuilder.AddColumn<bool>(
name: "IgnoreMultiTenancy",
table: "CacheManagementCacheItems",
nullable: false,
defaultValue: false);
}
protected override void Down(MigrationBuilder migrationBuilder)
{
migrationBuilder.DropColumn(
name: "CacheName",
table: "CacheManagementCacheItems");
migrationBuilder.DropColumn(
name: "IgnoreMultiTenancy",
table: "CacheManagementCacheItems");
migrationBuilder.AlterColumn<string>(
name: "DisplayName",
table: "CacheManagementCacheItems",
type: "nvarchar(max)",
nullable: true,
oldClrType: typeof(string));
migrationBuilder.AddColumn<string>(
name: "FullTypeName",
table: "CacheManagementCacheItems",
type: "nvarchar(max)",
nullable: true);
}
}
}
......@@ -25,6 +25,10 @@ namespace EasyAbp.CacheManagement.Migrations
.ValueGeneratedOnAdd()
.HasColumnType("uniqueidentifier");
b.Property<string>("CacheName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ConcurrencyStamp")
.IsConcurrencyToken()
.HasColumnName("ConcurrencyStamp")
......@@ -34,14 +38,15 @@ namespace EasyAbp.CacheManagement.Migrations
.HasColumnType("nvarchar(max)");
b.Property<string>("DisplayName")
.IsRequired()
.HasColumnType("nvarchar(max)");
b.Property<string>("ExtraProperties")
.HasColumnName("ExtraProperties")
.HasColumnType("nvarchar(max)");
b.Property<string>("FullTypeName")
.HasColumnType("nvarchar(max)");
b.Property<bool>("IgnoreMultiTenancy")
.HasColumnType("bit");
b.Property<bool>("TenantAllowed")
.HasColumnType("bit");
......
{
"ConnectionStrings": {
"Default": "Server=(LocalDb)\\MSSQLLocalDB;Database=CacheManagement_Unified;Trusted_Connection=True;MultipleActiveResultSets=true"
},
"Redis": {
"Configuration": "127.0.0.1"
}
}
\ No newline at end of file
using EasyAbp.CacheManagement.Localization;
using Volo.Abp.Authorization.Permissions;
using Volo.Abp.Localization;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.CacheManagement.Authorization
{
......@@ -15,6 +16,7 @@ namespace EasyAbp.CacheManagement.Authorization
cacheItems.AddChild(CacheManagementPermissions.CacheItems.Update, L("Permission:SetRead"));
cacheItems.AddChild(CacheManagementPermissions.CacheItems.Delete, L("Permission:Delete"));
cacheItems.AddChild(CacheManagementPermissions.CacheItems.ClearCache, L("Permission:ClearCache"));
cacheItems.AddChild(CacheManagementPermissions.CacheItems.ClearAllCache, L("Permission:ClearAllCache"), MultiTenancySides.Host);
}
private static LocalizableString L(string name)
......
......@@ -13,6 +13,7 @@ namespace EasyAbp.CacheManagement.Authorization
public const string Update = Default + ".Update";
public const string Create = Default + ".Create";
public const string ClearCache = Default + ".ClearCache";
public const string ClearAllCache = Default + ".ClearAllCache";
}
public static string[] GetAll()
......
......@@ -3,9 +3,12 @@ using System.ComponentModel;
namespace EasyAbp.CacheManagement.CacheItems.Dtos
{
public class ClearAllCacheItemDto
public class CacheItemDataDto
{
[DisplayName("CacheItemId")]
public Guid CacheItemId { get; set; }
public string CacheKey { get; set; }
public string CacheValue { get; set; }
}
}
\ No newline at end of file
......@@ -5,11 +5,13 @@ namespace EasyAbp.CacheManagement.CacheItems.Dtos
{
public class CacheItemDto : EntityDto<Guid>
{
public string FullTypeName { get; set; }
public string CacheName { get; set; }
public string DisplayName { get; set; }
public string Description { get; set; }
public bool IgnoreMultiTenancy { get; set; }
public bool TenantAllowed { get; set; }
}
......
......@@ -8,9 +8,5 @@ namespace EasyAbp.CacheManagement.CacheItems.Dtos
{
[DisplayName("CacheItemId")]
public Guid CacheItemId { get; set; }
[Required]
[DisplayName("CacheItemCacheKey")]
public string CacheKey { get; set; }
}
}
\ No newline at end of file
using System;
namespace EasyAbp.CacheManagement.CacheItems.Dtos
{
public class ClearCacheItemResultDto
{
public Guid CacheItemId { get; set; }
public long Count { get; set; }
}
}
\ No newline at end of file
using System;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
namespace EasyAbp.CacheManagement.CacheItems.Dtos
{
public class ClearSpecificCacheItemDto : ClearCacheItemDto
{
[Required]
[DisplayName("CacheItemCacheKey")]
public string CacheKey { get; set; }
}
}
\ No newline at end of file
......@@ -7,8 +7,8 @@ namespace EasyAbp.CacheManagement.CacheItems.Dtos
public class CreateUpdateCacheItemDto
{
[Required]
[DisplayName("CacheItemFullTypeName")]
public string FullTypeName { get; set; }
[DisplayName("CacheItemCacheName")]
public string CacheName { get; set; }
[Required]
[DisplayName("CacheItemDisplayName")]
......@@ -17,6 +17,9 @@ namespace EasyAbp.CacheManagement.CacheItems.Dtos
[DisplayName("CacheItemDescription")]
public string Description { get; set; }
[DisplayName("CacheItemIgnoreMultiTenancy")]
public bool IgnoreMultiTenancy { get; set; }
[DisplayName("CacheItemTenantAllowed")]
public bool TenantAllowed { get; set; }
}
......
......@@ -14,8 +14,14 @@ namespace EasyAbp.CacheManagement.CacheItems
CreateUpdateCacheItemDto,
CreateUpdateCacheItemDto>
{
Task<ClearCacheItemResultDto> ClearAsync(ClearCacheItemDto input);
Task<ListResultDto<CacheItemDataDto>> GetKeysAsync(Guid cacheItemId);
Task<CacheItemDataDto> GetDataAsync(Guid cacheItemId, string cacheKey);
Task ClearSpecificAsync(ClearSpecificCacheItemDto input);
Task ClearAsync(ClearCacheItemDto input);
Task<ClearCacheItemResultDto> ClearAllAsync(ClearAllCacheItemDto input);
Task ClearAllAsync();
}
}
\ No newline at end of file
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using EasyAbp.CacheManagement.Authorization;
using EasyAbp.CacheManagement.CacheItems.Dtos;
using Microsoft.AspNetCore.Authorization;
using Volo.Abp.Application.Dtos;
using Volo.Abp.Application.Services;
using Volo.Abp.Caching;
using Volo.Abp.MultiTenancy;
namespace EasyAbp.CacheManagement.CacheItems
{
......@@ -30,25 +30,59 @@ namespace EasyAbp.CacheManagement.CacheItems
_repository = repository;
}
[Authorize(CacheManagementPermissions.CacheItems.Default)]
public async Task<ListResultDto<CacheItemDataDto>> GetKeysAsync(Guid cacheItemId)
{
var cacheItem = await _repository.GetAsync(cacheItemId);
var keys = await _cacheItemManager.GetKeysAsync(cacheItem);
return new ListResultDto<CacheItemDataDto>(
new List<CacheItemDataDto>(keys.Select(key => new CacheItemDataDto
{CacheItemId = cacheItemId, CacheKey = key})));
}
[Authorize(CacheManagementPermissions.CacheItems.Default)]
public async Task<CacheItemDataDto> GetDataAsync(Guid cacheItemId, string cacheKey)
{
var cacheItem = await _repository.GetAsync(cacheItemId);
var keys = await _cacheItemManager.GetKeysAsync(cacheItem);
var key = keys.Single(key => key.Equals(cacheKey));
return new CacheItemDataDto
{
CacheItemId = cacheItemId,
CacheKey = cacheKey,
CacheValue = await _cacheItemManager.GetValueAsync(key)
};
}
[Authorize(CacheManagementPermissions.CacheItems.ClearCache)]
public async Task<ClearCacheItemResultDto> ClearAsync(ClearCacheItemDto input)
public async Task ClearSpecificAsync(ClearSpecificCacheItemDto input)
{
var cacheItem = await _repository.GetAsync(input.CacheItemId);
await AuthorizationService.CheckAsync(cacheItem, CacheManagementPermissions.CacheItems.ClearCache);
await _cacheItemManager.ClearAsync(cacheItem, input.CacheKey);
return new ClearCacheItemResultDto
{
CacheItemId = cacheItem.Id,
Count = 1
};
await _cacheItemManager.ClearSpecificAsync(cacheItem, input.CacheKey);
}
[Authorize(CacheManagementPermissions.CacheItems.ClearCache)]
public async Task ClearAsync(ClearCacheItemDto input)
{
var cacheItem = await _repository.GetAsync(input.CacheItemId);
await AuthorizationService.CheckAsync(cacheItem, CacheManagementPermissions.CacheItems.ClearCache);
await _cacheItemManager.ClearAsync(cacheItem);
}
public async Task<ClearCacheItemResultDto> ClearAllAsync(ClearAllCacheItemDto input)
[Authorize(CacheManagementPermissions.CacheItems.ClearAllCache)]
public async Task ClearAllAsync()
{
throw new NotImplementedException();
await _cacheItemManager.ClearAllAsync();
}
}
}
\ No newline at end of file
......@@ -3,13 +3,21 @@
"texts": {
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
{
"culture": "en",
"texts": {
"ManageYourProfile": "Manage your profile",
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
......@@ -3,13 +3,21 @@
"texts": {
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
......@@ -3,13 +3,21 @@
"texts": {
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
{
"culture": "sl",
"texts": {
"ManageYourProfile": "Upravljajte svojim profilom",
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
{
"culture": "tr",
"texts": {
"ManageYourProfile": "Profil y�netimi",
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
......@@ -3,13 +3,21 @@
"texts": {
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
{
"culture": "zh-Hans",
"texts": {
"ManageYourProfile": "管理个人资料",
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
{
"culture": "zh-Hant",
"texts": {
"ManageYourProfile": "管理個人資料",
"Menu:CacheItem": "MenuCacheItem",
"CacheItem": "CacheItem",
"CacheItemFullTypeName": "CacheItemFullTypeName",
"CacheItemCacheName": "CacheItemCacheName",
"CacheItemDisplayName": "CacheItemDisplayName",
"CacheItemDescription": "CacheItemDescription",
"CacheItemIgnoreMultiTenancy": "CacheItemIgnoreMultiTenancy",
"CacheItemTenantAllowed": "CacheItemTenantAllowed",
"CacheItemCacheKey": "CacheItemCacheKey",
"CacheItemCacheValue": "CacheItemCacheValue",
"CreateCacheItem": "CreateCacheItem",
"EditCacheItem": "EditCacheItem",
"CacheItemDeletionConfirmationMessage": "Are you sure to delete the cacheitem {0}?",
"SuccessfullyDeleted": "Successfully deleted"
"SuccessfullyDeleted": "Successfully deleted",
"SuccessCleared": "Successfully cleared",
"ClearCache": "ClearCache",
"ClearAllCache": "ClearAllCache",
"ClearCacheConfirmationMessage": "Are you sure to clear the cache {0}?",
"ClearAllCacheConfirmationMessage": "Are you sure to clear all caches?"
}
}
\ No newline at end of file
......@@ -7,13 +7,15 @@ namespace EasyAbp.CacheManagement.CacheItems
public class CacheItem : AggregateRoot<Guid>
{
[NotNull]
public virtual string FullTypeName { get; protected set; }
public virtual string CacheName { get; protected set; }
[NotNull]
public virtual string DisplayName { get; protected set; }
[CanBeNull]
public virtual string Description { get; protected set; }
public virtual bool IgnoreMultiTenancy { get; protected set; }
public virtual bool TenantAllowed { get; protected set; }
......@@ -23,15 +25,17 @@ namespace EasyAbp.CacheManagement.CacheItems
public CacheItem(
Guid id,
[NotNull] string fullTypeName,
[NotNull] string cacheName,
[NotNull] string displayName,
[CanBeNull] string description,
bool ignoreMultiTenancy,
bool tenantAllowed
) :base(id)
{
FullTypeName = fullTypeName;
CacheName = cacheName;
DisplayName = displayName;
Description = description;
IgnoreMultiTenancy = ignoreMultiTenancy;
TenantAllowed = tenantAllowed;
}
}
......
using System;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.Caching.Distributed;
using Volo.Abp.Caching;
using Volo.Abp.Domain.Services;
using Volo.Abp.MultiTenancy;
using Volo.Abp.Threading;
namespace EasyAbp.CacheManagement.CacheItems
{
public class CacheItemManager : DomainService, ICacheItemManager
{
private readonly IDistributedCache _distributedCache;
private readonly ICancellationTokenProvider _cancellationTokenProvider;
private readonly IDistributedCacheKeyNormalizer _keyNormalizer;
public CacheItemManager(
IDistributedCache distributedCache,
ICancellationTokenProvider cancellationTokenProvider,
IDistributedCacheKeyNormalizer keyNormalizer)
{
_distributedCache = distributedCache;
_cancellationTokenProvider = cancellationTokenProvider;
_keyNormalizer = keyNormalizer;
}
public async Task ClearAsync(CacheItem cacheItem, string cacheKey, CancellationToken cancellationToken = default)
{
var type = Type.GetType(cacheItem.FullTypeName);
if (type == null)
{
throw new CacheItemTypeNotFoundException(cacheItem.FullTypeName);
}
var normalizedKey = _keyNormalizer.NormalizeKey(
new DistributedCacheKeyNormalizeArgs(
cacheKey,
CacheNameAttribute.GetCacheName(type),
type.IsDefined(typeof(IgnoreMultiTenancyAttribute), true)
)
);
await _distributedCache.RemoveAsync(normalizedKey, _cancellationTokenProvider.FallbackToProvider());
}
}
}
\ No newline at end of file
using System;
namespace EasyAbp.CacheManagement.CacheItems
{
public class DistributedCacheProviderInvalidException : ApplicationException
{
}
}
\ No newline at end of file
using System.Threading;
using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks;
using Volo.Abp.Domain.Services;
......@@ -6,6 +7,14 @@ namespace EasyAbp.CacheManagement.CacheItems
{
public interface ICacheItemManager : IDomainService
{
Task ClearAsync(CacheItem cacheItem, string cacheKey, CancellationToken cancellationToken = default);
Task<IEnumerable<string>> GetKeysAsync(CacheItem cacheItem, CancellationToken cancellationToken = default);
Task ClearAllAsync(CancellationToken cancellationToken = default);
Task ClearAsync(CacheItem cacheItem, CancellationToken cancellationToken = default);
Task ClearSpecificAsync(CacheItem cacheItem, string cacheKey, CancellationToken cancellationToken = default);
Task<string> GetValueAsync(string cacheKey, CancellationToken cancellationToken = default);
}
}
\ No newline at end of file
......@@ -46,6 +46,8 @@ namespace EasyAbp.CacheManagement.EntityFrameworkCore
b.ToTable(options.TablePrefix + "CacheItems", options.Schema);
b.ConfigureByConvention();
/* Configure more properties here */
b.Property(c => c.CacheName).IsRequired();
b.Property(c => c.DisplayName).IsRequired();
});
}
}
......
......@@ -5,13 +5,20 @@
@{
Layout = null;
}
<script>
let cacheItemId = '@Model.CacheItemId';
</script>
<abp-dynamic-form abp-model="ViewModel" data-ajaxForm="true" asp-page="ClearCacheModal">
<abp-modal>
<abp-modal-header title="@L["ClearCacheCacheItem"].Value"></abp-modal-header>
<abp-modal-header title="@L["ClearCache"].Value"></abp-modal-header>
<abp-modal-body>
<abp-input asp-for="CacheItemId" />
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Cancel|AbpModalButtons.Save)"></abp-modal-footer>
<abp-modal-footer>
<abp-button data-dismiss="modal" button-type="Secondary">@L["ClearAllCache"].Value</abp-button>
<abp-button type="submit" button-type="Danger" busy-text="@L["ClearAllCache"].Value">@L["ClearAllCache"].Value</abp-button>
</abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
\ No newline at end of file
......@@ -13,7 +13,6 @@ namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem
[BindProperty(SupportsGet = true)]
public Guid CacheItemId { get; set; }
[BindProperty]
public ClearCacheItemViewModel ViewModel { get; set; }
private readonly ICacheItemAppService _service;
......@@ -22,13 +21,22 @@ namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem
{
_service = service;
}
public async Task OnGetAsync()
{
var cacheItemDto = await _service.GetAsync(CacheItemId);
ViewModel = new ClearCacheItemViewModel
{
DisplayName = cacheItemDto.DisplayName
};
}
public async Task<IActionResult> OnPostAsync()
{
await _service.ClearAsync(new ClearCacheItemDto
await _service.ClearAsync(new ClearCacheItemDto()
{
CacheItemId = CacheItemId,
CacheKey = ViewModel.CacheKey
});
return NoContent();
......
@page
@inherits EasyAbp.CacheManagement.Web.Pages.CacheManagementPage
@using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Modal
@model EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem.DetailModalModel
@{
Layout = null;
}
<abp-dynamic-form abp-model="Data" data-ajaxForm="true" asp-page="DetailModal">
<abp-modal>
<abp-modal-header title="@L["CacheItem"].Value"></abp-modal-header>
<abp-modal-body>
<abp-form-content />
</abp-modal-body>
<abp-modal-footer buttons="@(AbpModalButtons.Close)"></abp-modal-footer>
</abp-modal>
</abp-dynamic-form>
\ No newline at end of file
using System;
using System.Threading.Tasks;
using EasyAbp.CacheManagement.CacheItems;
using EasyAbp.CacheManagement.CacheItems.Dtos;
using EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem.ViewModels;
using Microsoft.AspNetCore.Mvc;
namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem
{
public class DetailModalModel : CacheManagementPageModel
{
[BindProperty]
public CacheItemDataViewModel Data { get; set; }
private readonly ICacheItemAppService _service;
public DetailModalModel(ICacheItemAppService service)
{
_service = service;
}
public async Task OnGetAsync(Guid cacheItemId, string cacheKey)
{
var data = await _service.GetDataAsync(cacheItemId, cacheKey);
Data = new CacheItemDataViewModel
{
CacheKey = data.CacheKey,
CacheValue = data.CacheValue
};
}
}
}
\ No newline at end of file
@page
@using EasyAbp.CacheManagement.Authorization
@using Microsoft.AspNetCore.Authorization
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@inherits EasyAbp.CacheManagement.Web.Pages.CacheManagementPage
@model EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem.IndexModel
@inject IAuthorizationService Authorization
@inject IPageLayout PageLayout
@{
PageLayout.Content.Title = L["CacheItem"].Value;
......@@ -25,10 +28,13 @@
<abp-card-title>@L["CacheItem"]</abp-card-title>
</abp-column>
<abp-column size-md="_6" class="text-right">
<abp-button id="NewCacheItemButton"
text="@L["CreateCacheItem"].Value"
icon="plus"
button-type="Primary" />
@if (await Authorization.IsGrantedAsync(CacheManagementPermissions.CacheItems.Create))
{
<abp-button id="NewCacheItemButton"
text="@L["CreateCacheItem"].Value"
icon="plus"
button-type="Primary"/>
}
</abp-column>
</abp-row>
</abp-card-header>
......
@page
@using Volo.Abp.AspNetCore.Mvc.UI.Layout
@inherits EasyAbp.CacheManagement.Web.Pages.CacheManagementPage
@model EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem.KeyListModel
@inject IPageLayout PageLayout
@{
PageLayout.Content.Title = L["CacheItemData"].Value;
PageLayout.Content.BreadCrumb.Add(L["Menu:CacheItem"].Value);
PageLayout.Content.MenuItemName = "CacheItem";
}
@section scripts
{
<abp-script src="/Pages/CacheManagement/CacheItems/CacheItem/keyList.js" />
}
@section styles
{
<abp-style src="/Pages/CacheManagement/CacheItems/CacheItem/keyList.css"/>
}
<script>
let cacheItemId = '@Model.CacheItem.Id'
</script>
<abp-card>
<abp-card-header>
<abp-row>
<abp-column size-md="_6">
<abp-card-title>@L["CacheItemData"] - @Model.CacheItem.DisplayName</abp-card-title>
</abp-column>
<abp-column size-md="_6" class="text-right">
</abp-column>
</abp-row>
</abp-card-header>
<abp-card-body>
<abp-table striped-rows="true" id="CacheItemTable" class="nowrap">
<thead>
<tr>
<th>@L["Actions"]</th>
<th>@L["CacheKey"]</th>
</tr>
</thead>
</abp-table>
</abp-card-body>
</abp-card>
\ No newline at end of file
using System;
using System.Threading.Tasks;
using EasyAbp.CacheManagement.CacheItems;
using EasyAbp.CacheManagement.CacheItems.Dtos;
namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem
{
public class KeyListModel : CacheManagementPageModel
{
private readonly ICacheItemAppService _service;
public CacheItemDto CacheItem { get; set; }
public KeyListModel(ICacheItemAppService service)
{
_service = service;
}
public async Task OnGetAsync(Guid cacheItemId)
{
CacheItem = await _service.GetAsync(cacheItemId);
await Task.CompletedTask;
}
}
}
using System.ComponentModel;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem.ViewModels
{
public class CacheItemDataViewModel
{
[DisabledInput]
[DisplayName("CacheItemCacheKey")]
public string CacheKey { get; set; }
[DisabledInput]
[TextArea(Rows = 4)]
[DisplayName("CacheItemCacheValue")]
public string CacheValue { get; set; }
}
}
\ No newline at end of file
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using Volo.Abp.AspNetCore.Mvc.UI.Bootstrap.TagHelpers.Form;
namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem.ViewModels
{
public class ClearCacheItemViewModel
{
[Required]
[DisplayName("CacheItemCacheKey")]
public string CacheKey { get; set; }
[DisabledInput]
[DisplayName("CacheItemDisplayName")]
public string DisplayName { get; set; }
}
}
\ No newline at end of file
......@@ -7,8 +7,8 @@ namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem
public class CreateEditCacheItemViewModel
{
[Required]
[DisplayName("CacheItemFullTypeName")]
public string FullTypeName { get; set; }
[DisplayName("CacheItemCacheName")]
public string CacheName { get; set; }
[Required]
[DisplayName("CacheItemDisplayName")]
......@@ -18,6 +18,9 @@ namespace EasyAbp.CacheManagement.Web.Pages.CacheManagement.CacheItems.CacheItem
[TextArea(Rows = 4)]
public string Description { get; set; }
[DisplayName("CacheItemIgnoreMultiTenancy")]
public bool IgnoreMultiTenancy { get; set; }
[DisplayName("CacheItemTenantAllowed")]
public bool TenantAllowed { get; set; }
}
......
......@@ -21,6 +21,12 @@ $(function () {
rowAction: {
items:
[
{
text: l('CacheItemData'),
action: function (data) {
document.location.href = "CacheItem/KeyList?cacheItemId=" + data.record.id
}
},
{
text: l('ClearCache'),
action: function (data) {
......
$(function () {
var l = abp.localization.getResource('CacheManagement');
var detailModal = new abp.ModalManager(abp.appPath + 'CacheManagement/CacheItems/CacheItem/DetailModal');
var service = easyAbp.cacheManagement.cacheItems.cacheItem;
var dataTable = $('#CacheItemTable').DataTable(abp.libs.datatables.normalizeConfiguration({
processing: true,
bPaginate: false,
searching: false,
autoWidth: false,
scrollCollapse: true,
order: [[1, "asc"]],
ajax: abp.libs.datatables.createAjax(service.getKeys, function () {
return cacheItemId;
}),
columnDefs: [
{
rowAction: {
items:
[
{
text: l('Detail'),
action: function (data) {
detailModal.open({ cacheItemId:data.record.cacheItemId, cacheKey: data.record.cacheKey });
}
},
{
text: l('ClearCache'),
confirmMessage: function (data) {
return l('ClearCacheConfirmationMessage', data.record.cacheKey);
},
action: function (data) {
service.clearSpecific({
cacheItemId: data.record.cacheItemId,
cacheKey: data.record.cacheKey
})
.then(function () {
abp.notify.info(l('SuccessCleared'));
dataTable.ajax.reload();
});
}
}
]
}
},
{ data: "cacheKey" },
]
}));
});
\ No newline at end of file
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\..\common.props" />
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment