diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index 1f4c9e23..ef7049cc 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -44,5 +44,5 @@ jobs:
run: |
for file in nugetpkgs/*.nupkg
do
- dotnet nuget push $file -k ${{ secrets.MYGET_API_KEY }} --skip-duplicate -s https://www.myget.org/F/ncc/api/v2/package
+ dotnet nuget push $file -k ${{ secrets.NUGET_API_KEY }} --skip-duplicate -s https://www.nuget.org/api/v2/package
done
\ No newline at end of file
diff --git a/EasyCaching.sln b/EasyCaching.sln
index a595298f..ff571815 100644
--- a/EasyCaching.sln
+++ b/EasyCaching.sln
@@ -1,6 +1,6 @@
Microsoft Visual Studio Solution File, Format Version 12.00
-# Visual Studio Version 16
-VisualStudioVersion = 16.0.29324.140
+# Visual Studio Version 17
+VisualStudioVersion = 17.2.32616.157
MinimumVisualStudioVersion = 10.0.40219.1
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A0F5CC7E-155F-4726-8DEB-E966950B3FE9}"
EndProject
@@ -70,6 +70,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyCaching.LiteDB", "src\E
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyCaching.Serialization.SystemTextJson", "serialization\EasyCaching.Serialization.SystemTextJson\EasyCaching.Serialization.SystemTextJson.csproj", "{4FCF16BF-5E21-4B74-AB45-3C121ADF1485}"
EndProject
+Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "EasyCaching.Bus.ConfluentKafka", "bus\EasyCaching.Bus.ConfluentKafka\EasyCaching.Bus.ConfluentKafka.csproj", "{F7FBADEB-D766-4595-949A-07104B52692C}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Bus.Zookeeper", "bus\EasyCaching.Bus.Zookeeper\EasyCaching.Bus.Zookeeper.csproj", "{5E488583-391E-4E15-83C1-7301B4FE79AE}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -184,6 +188,14 @@ Global
{4FCF16BF-5E21-4B74-AB45-3C121ADF1485}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4FCF16BF-5E21-4B74-AB45-3C121ADF1485}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4FCF16BF-5E21-4B74-AB45-3C121ADF1485}.Release|Any CPU.Build.0 = Release|Any CPU
+ {F7FBADEB-D766-4595-949A-07104B52692C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {F7FBADEB-D766-4595-949A-07104B52692C}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {F7FBADEB-D766-4595-949A-07104B52692C}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {F7FBADEB-D766-4595-949A-07104B52692C}.Release|Any CPU.Build.0 = Release|Any CPU
+ {5E488583-391E-4E15-83C1-7301B4FE79AE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {5E488583-391E-4E15-83C1-7301B4FE79AE}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {5E488583-391E-4E15-83C1-7301B4FE79AE}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {5E488583-391E-4E15-83C1-7301B4FE79AE}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
@@ -216,6 +228,8 @@ Global
{711603E1-8363-4F8D-9AA9-8C03EC8BD35F} = {B4241D34-A973-4A13-BD89-9BAE3F2BDDF6}
{BA850294-3103-4540-8A27-FC768E1DC8FC} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
{4FCF16BF-5E21-4B74-AB45-3C121ADF1485} = {15070C49-A507-4844-BCFE-D319CFBC9A63}
+ {F7FBADEB-D766-4595-949A-07104B52692C} = {B337509B-75F9-4851-821F-9BBE87C4E4BC}
+ {5E488583-391E-4E15-83C1-7301B4FE79AE} = {B337509B-75F9-4851-821F-9BBE87C4E4BC}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {63A57886-054B-476C-AAE1-8D7C8917682E}
diff --git a/build/releasenotes.props b/build/releasenotes.props
index 2eff3fe0..f2f98711 100644
--- a/build/releasenotes.props
+++ b/build/releasenotes.props
@@ -1,23 +1,27 @@
- 1. Add CancellationToken for async methods.
+ 1. Remove BinaryFormatter.
+ 2. Support removing cache keys by pattern.
- 1. Add CancellationToken for async methods.
+ 1. Upgrading dependencies.
- 1. Add CancellationToken for async methods.
- 2. Add persist key method to redis providers.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
- 1. Add CancellationToken for async methods.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
- 1. Add CancellationToken for async methods.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
- 1. Add CancellationToken for async methods.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
1. Upgrading dependencies.
@@ -38,11 +42,12 @@
1. Upgrading dependencies.
- 1. Add CancellationToken for async methods.
- 2. Add persist key method to redis providers.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
1. Upgrading dependencies.
@@ -50,11 +55,19 @@
1. Upgrading dependencies.
+
+ 1. Add EasyCachingKafkaBus.
+
+
+ 1. Add EasyCachingZookeeperBus.
+
- 1. Add CancellationToken for async methods.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
- 1. Add CancellationToken for async methods.
+ 1. Upgrading dependencies.
+ 2. Support removing cache keys by pattern.
1. Upgrading dependencies.
diff --git a/build/version.props b/build/version.props
index 0bb59cda..dee60423 100644
--- a/build/version.props
+++ b/build/version.props
@@ -1,24 +1,26 @@
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
- 1.6.1
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
+ 1.7.0
diff --git a/bus/EasyCaching.Bus.CSRedis/Configurations/CSRedisOptionsExtension.cs b/bus/EasyCaching.Bus.CSRedis/Configurations/CSRedisOptionsExtension.cs
index b56920af..7dcffe0d 100644
--- a/bus/EasyCaching.Bus.CSRedis/Configurations/CSRedisOptionsExtension.cs
+++ b/bus/EasyCaching.Bus.CSRedis/Configurations/CSRedisOptionsExtension.cs
@@ -41,8 +41,6 @@ public void AddServices(IServiceCollection services)
{
services.AddOptions();
- services.TryAddSingleton();
-
services.Configure(_name, _configure);
services.AddSingleton(x =>
diff --git a/bus/EasyCaching.Bus.ConfluentKafka/Configurations/ConfluentKafkaBusOptions.cs b/bus/EasyCaching.Bus.ConfluentKafka/Configurations/ConfluentKafkaBusOptions.cs
new file mode 100644
index 00000000..27c35007
--- /dev/null
+++ b/bus/EasyCaching.Bus.ConfluentKafka/Configurations/ConfluentKafkaBusOptions.cs
@@ -0,0 +1,39 @@
+using Confluent.Kafka;
+
+namespace EasyCaching.Bus.ConfluentKafka
+{
+ ///
+ /// kafka bus options
+ ///
+ public class ConfluentKafkaBusOptions
+ {
+ ///
+ /// kafka address(BootstrapServers must)
+ ///
+ public string BootstrapServers { get; set; }
+
+
+ ///
+ /// kafka bus producer options.
+ ///
+ public ProducerConfig ProducerConfig { get; set; }
+
+ ///
+ /// kafka bus consumer options.(if GroupId value below is empty,then ConsumerConfig.GroupId must )
+ ///
+ public ConsumerConfig ConsumerConfig { get; set; }
+
+ ///
+ /// kafka bus consumer options with consumer groupId
+ /// (if ConsumerConfig below has give GroupId value , this options can ignore)
+ /// import:if application is cluster,you should set this different value in application,this will make consumer can consumerdata
+ ///
+ public string GroupId { get; set; }
+
+ ///
+ /// kafka bus consumer consume count
+ ///
+ public int ConsumerCount { get; set; } = 1;
+ }
+
+}
diff --git a/bus/EasyCaching.Bus.ConfluentKafka/Configurations/ConfluentKafkaOptionsExtension.cs b/bus/EasyCaching.Bus.ConfluentKafka/Configurations/ConfluentKafkaOptionsExtension.cs
new file mode 100644
index 00000000..a810bbfb
--- /dev/null
+++ b/bus/EasyCaching.Bus.ConfluentKafka/Configurations/ConfluentKafkaOptionsExtension.cs
@@ -0,0 +1,43 @@
+namespace EasyCaching.Bus.ConfluentKafka
+{
+ using System;
+ using EasyCaching.Core.Bus;
+ using EasyCaching.Core.Configurations;
+ using EasyCaching.Core.Serialization;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.DependencyInjection.Extensions;
+
+ ///
+ /// Kafka options extension.
+ ///
+ internal sealed class ConfluentKafkaOptionsExtension : IEasyCachingOptionsExtension
+ {
+
+ private readonly Action _confluentKafkaBusOptions;
+
+ public ConfluentKafkaOptionsExtension(Action confluentKafkaBusOptions)
+ {
+ this._confluentKafkaBusOptions = confluentKafkaBusOptions;
+ }
+
+ ///
+ /// Adds the services.
+ ///
+ /// Services.
+ public void AddServices(IServiceCollection services)
+ {
+ services.AddOptions();
+
+ services.AddOptions()
+ .Configure(_confluentKafkaBusOptions);
+
+
+ //var options = services.BuildServiceProvider()
+ // .GetRequiredService>()
+ // .Value;
+
+ services.AddSingleton();
+
+ }
+ }
+}
diff --git a/bus/EasyCaching.Bus.ConfluentKafka/Configurations/EasyCachingOptionsExtensions.cs b/bus/EasyCaching.Bus.ConfluentKafka/Configurations/EasyCachingOptionsExtensions.cs
new file mode 100644
index 00000000..4fd1cb98
--- /dev/null
+++ b/bus/EasyCaching.Bus.ConfluentKafka/Configurations/EasyCachingOptionsExtensions.cs
@@ -0,0 +1,81 @@
+namespace Microsoft.Extensions.DependencyInjection
+{
+ using Confluent.Kafka;
+ using EasyCaching.Bus.ConfluentKafka;
+ using EasyCaching.Core;
+ using EasyCaching.Core.Configurations;
+ using Microsoft.Extensions.Configuration;
+ using System;
+
+ ///
+ /// EasyCaching options extensions.
+ ///
+ public static class EasyCachingOptionsExtensions
+ {
+ ///
+ /// Withs the ConfluentKafka bus (specify the config via hard code).
+ ///
+ ///
+ ///
+ ///
+ public static EasyCachingOptions WithConfluentKafkaBus(
+ this EasyCachingOptions options
+ , Action configure
+ )
+ {
+ ArgumentCheck.NotNull(configure, nameof(configure));
+ //option convert
+ ConfluentKafkaBusOptions kafkaOptions = new ConfluentKafkaBusOptions();
+ configure.Invoke(kafkaOptions);
+ void kafkaBusConfigure(ConfluentKafkaBusOptions x)
+ {
+ x.BootstrapServers = kafkaOptions.BootstrapServers;
+ x.ProducerConfig = kafkaOptions.ProducerConfig ?? new ProducerConfig();
+ x.ConsumerConfig = kafkaOptions.ConsumerConfig ?? new ConsumerConfig();
+ //address
+ x.ProducerConfig.BootstrapServers = x.ProducerConfig.BootstrapServers ?? kafkaOptions.BootstrapServers;
+ x.ConsumerConfig.BootstrapServers = x.ConsumerConfig.BootstrapServers ?? kafkaOptions.BootstrapServers;
+ //consumer groupId
+ x.ConsumerConfig.GroupId = x.ConsumerConfig.GroupId ?? kafkaOptions.GroupId;
+ x.ConsumerConfig.AutoOffsetReset = kafkaOptions.ConsumerConfig.AutoOffsetReset ?? AutoOffsetReset.Latest;
+ }
+
+ options.RegisterExtension(new ConfluentKafkaOptionsExtension(kafkaBusConfigure));
+ return options;
+ }
+
+ ///
+ /// Withs the ConfluentKafka bus (read config from configuration file).
+ ///
+ ///
+ ///
+ /// The section name in the configuration file.
+ ///
+ public static EasyCachingOptions WithConfluentKafkaBus(
+ this EasyCachingOptions options
+ , IConfiguration configuration
+ , string sectionName = EasyCachingConstValue.KafkaBusSection
+ )
+ {
+ var dbConfig = configuration.GetSection(sectionName);
+ var kafkaOptions = new ConfluentKafkaBusOptions();
+ dbConfig.Bind(kafkaOptions);
+
+ void configure(ConfluentKafkaBusOptions x)
+ {
+ x.BootstrapServers = kafkaOptions.BootstrapServers;
+ x.ProducerConfig = kafkaOptions.ProducerConfig ?? new ProducerConfig();
+ x.ConsumerConfig = kafkaOptions.ConsumerConfig ?? new ConsumerConfig();
+ //address
+ x.ProducerConfig.BootstrapServers = x.ProducerConfig.BootstrapServers ?? kafkaOptions.BootstrapServers;
+ x.ConsumerConfig.BootstrapServers = x.ConsumerConfig.BootstrapServers ?? kafkaOptions.BootstrapServers;
+ //consumer groupId
+ x.ConsumerConfig.GroupId = x.ConsumerConfig.GroupId ?? kafkaOptions.GroupId;
+ x.ConsumerConfig.AutoOffsetReset = kafkaOptions.ConsumerConfig.AutoOffsetReset ?? AutoOffsetReset.Latest;
+ }
+
+ options.RegisterExtension(new ConfluentKafkaOptionsExtension(configure));
+ return options;
+ }
+ }
+}
diff --git a/bus/EasyCaching.Bus.ConfluentKafka/DefaultConfluentKafkaBus.cs b/bus/EasyCaching.Bus.ConfluentKafka/DefaultConfluentKafkaBus.cs
new file mode 100644
index 00000000..296fd5b2
--- /dev/null
+++ b/bus/EasyCaching.Bus.ConfluentKafka/DefaultConfluentKafkaBus.cs
@@ -0,0 +1,149 @@
+namespace EasyCaching.Bus.ConfluentKafka
+{
+ using System;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using Confluent.Kafka;
+ using EasyCaching.Core;
+ using EasyCaching.Core.Bus;
+ using EasyCaching.Core.Serialization;
+ using Microsoft.Extensions.Logging;
+ using Microsoft.Extensions.Logging.Abstractions;
+ using Microsoft.Extensions.Options;
+
+ public class DefaultConfluentKafkaBus : EasyCachingAbstractBus
+ {
+
+
+ ///
+ /// The kafka Bus options.
+ ///
+ private readonly ConfluentKafkaBusOptions _kafkaBusOptions;
+
+ ///
+ /// The serializer.
+ ///
+ private readonly IEasyCachingSerializer _serializer;
+
+ ///
+ /// kafka producer object
+ ///
+
+ private readonly IProducer _producer;
+
+
+ ///
+ /// log
+ ///
+
+ private readonly ILogger _logger = NullLogger.Instance;
+
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public DefaultConfluentKafkaBus(
+ IOptionsMonitor kafkaBusOptions
+ , IEasyCachingSerializer serializer)
+ {
+ this.BusName = "easycachingbus";
+ this._kafkaBusOptions = kafkaBusOptions.CurrentValue;
+
+ this._producer = new ProducerBuilder(this._kafkaBusOptions.ProducerConfig).Build();
+
+ this._serializer = serializer;
+ }
+
+ ///
+ /// Publish the specified topic and message.
+ ///
+ /// Topic.
+ /// Message.
+ public override void BasePublish(string topic, EasyCachingMessage message)
+ {
+ var msg = _serializer.Serialize(message);
+
+ _producer.Produce(topic, new Message { Value = msg });
+ }
+
+ ///
+ /// Publishs the async.
+ ///
+ /// The async.
+ /// Topic.
+ /// Message.
+ /// Cancellation token.
+ public override async Task BasePublishAsync(string topic, EasyCachingMessage message, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var msg = _serializer.Serialize(message);
+
+ await _producer.ProduceAsync(topic, new Message { Value = msg });
+ }
+
+ ///
+ /// Subscribe the specified topic and action.
+ ///
+ /// Topic.
+ /// Action.
+ public override void BaseSubscribe(string topic, Action action)
+ {
+ Task.Factory.StartNew(() =>
+ {
+ for (int i = 0; i < this._kafkaBusOptions.ConsumerCount; i++)
+ {
+ using (var consumer = new ConsumerBuilder(this._kafkaBusOptions.ConsumerConfig).Build())
+ {
+ consumer.Subscribe(topic);
+ try
+ {
+ while (true)
+ {
+ try
+ {
+ var cr = consumer.Consume();
+ if (cr.IsPartitionEOF
+ || cr.Message == null
+ || cr.Message.Value.Length == 0)
+ {
+ continue;
+ }
+ OnMessage(cr.Message.Value);
+ }
+ catch (ConsumeException ex)
+ {
+ _logger.LogError(ex, "Consumer {0} error of reason {1}.", topic, ex.Error.Reason);
+ }
+ catch (OperationCanceledException)
+ {
+ throw;
+ }
+ catch (Exception ex)
+ {
+ _logger.LogError(ex, "Consumer {0} error.", topic);
+ }
+ }
+ }
+ catch (OperationCanceledException ex)
+ {
+ _logger.LogWarning(ex, "Consumer {0} cancel.", topic);
+ consumer.Close();
+ }
+ }
+ }
+ }, TaskCreationOptions.LongRunning);
+ }
+
+ ///
+ /// Ons the message.
+ ///
+ /// Body.
+ private void OnMessage(byte[] body)
+ {
+ var message = _serializer.Deserialize(body);
+
+ BaseOnMessage(message);
+ }
+ }
+}
diff --git a/bus/EasyCaching.Bus.ConfluentKafka/EasyCaching.Bus.ConfluentKafka.csproj b/bus/EasyCaching.Bus.ConfluentKafka/EasyCaching.Bus.ConfluentKafka.csproj
new file mode 100644
index 00000000..52779cf2
--- /dev/null
+++ b/bus/EasyCaching.Bus.ConfluentKafka/EasyCaching.Bus.ConfluentKafka.csproj
@@ -0,0 +1,42 @@
+
+
+
+
+ netstandard2.0;net6.0
+ ncc;Catcher Wong
+ ncc;Catcher Wong
+ $(EasyCachingKafkaBusPackageVersion)
+
+
+ A simple caching bus(message bus) based on Kafka.
+
+ Bus,Hybrid,Kafka,Caching,Cache
+ https://github.com/dotnetcore/EasyCaching
+ LICENSE
+ https://github.com/dotnetcore/EasyCaching
+ https://github.com/dotnetcore/EasyCaching
+ nuget-icon.png
+
+ $(EasyCachingKafkaBusPackageNotes)
+
+
+
+
+ true
+ $(NoWarn);1591
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/bus/EasyCaching.Bus.RabbitMQ/Configurations/RabbitMQBusOptionsExtension.cs b/bus/EasyCaching.Bus.RabbitMQ/Configurations/RabbitMQBusOptionsExtension.cs
index c77edd42..8891ec70 100644
--- a/bus/EasyCaching.Bus.RabbitMQ/Configurations/RabbitMQBusOptionsExtension.cs
+++ b/bus/EasyCaching.Bus.RabbitMQ/Configurations/RabbitMQBusOptionsExtension.cs
@@ -37,7 +37,6 @@ public void AddServices(IServiceCollection services)
services.AddOptions();
services.Configure(configure);
- services.TryAddSingleton();
services.AddSingleton, ModelPooledObjectPolicy>();
services.AddSingleton();
}
diff --git a/bus/EasyCaching.Bus.Redis/Configurations/RedisBusOptionsExtension.cs b/bus/EasyCaching.Bus.Redis/Configurations/RedisBusOptionsExtension.cs
index fe06b869..6e18172b 100644
--- a/bus/EasyCaching.Bus.Redis/Configurations/RedisBusOptionsExtension.cs
+++ b/bus/EasyCaching.Bus.Redis/Configurations/RedisBusOptionsExtension.cs
@@ -44,7 +44,6 @@ public void AddServices(IServiceCollection services)
services.AddOptions();
services.Configure(_name, configure);
- services.TryAddSingleton();
services.AddSingleton(x =>
{
var optionsMon = x.GetRequiredService>();
diff --git a/bus/EasyCaching.Bus.Zookeeper/Configurations/EasyCachingOptionsExtensions.cs b/bus/EasyCaching.Bus.Zookeeper/Configurations/EasyCachingOptionsExtensions.cs
new file mode 100644
index 00000000..80d7452a
--- /dev/null
+++ b/bus/EasyCaching.Bus.Zookeeper/Configurations/EasyCachingOptionsExtensions.cs
@@ -0,0 +1,64 @@
+namespace Microsoft.Extensions.DependencyInjection
+{
+ using EasyCaching.Bus.Zookeeper;
+ using EasyCaching.Core;
+ using EasyCaching.Core.Configurations;
+ using Microsoft.Extensions.Configuration;
+ using System;
+
+ ///
+ /// EasyCaching options extensions.
+ ///
+ public static class EasyCachingOptionsExtensions
+ {
+ ///
+ /// Withs the Zookeeper bus (specify the config via hard code).
+ ///
+ ///
+ ///
+ ///
+ public static EasyCachingOptions WithZookeeeperBus(
+ this EasyCachingOptions options
+ , Action configure
+ )
+ {
+ ArgumentCheck.NotNull(configure, nameof(configure));
+ options.RegisterExtension(new ZookeeperOptionsExtension(configure));
+ return options;
+ }
+
+ ///
+ /// Withs the zookeeper bus (read config from configuration file).
+ ///
+ ///
+ ///
+ /// The section name in the configuration file.
+ ///
+ public static EasyCachingOptions WithConfluentKafkaBus(
+ this EasyCachingOptions options
+ , IConfiguration configuration
+ , string sectionName = EasyCachingConstValue.ZookeeperBusSection
+ )
+ {
+ var dbConfig = configuration.GetSection(sectionName);
+ var zkOptions = new ZkBusOptions();
+ dbConfig.Bind(zkOptions);
+
+ void configure(ZkBusOptions x)
+ {
+ x.ConnectionString = zkOptions.ConnectionString;
+ x.SessionTimeout = zkOptions.SessionTimeout;
+ x.OperatingTimeout = zkOptions.OperatingTimeout;
+ x.ConnectionTimeout = zkOptions.ConnectionTimeout;
+ x.Digest = zkOptions.Digest;
+ x.BaseRoutePath = zkOptions.BaseRoutePath;
+ x.ReadOnly = zkOptions.ReadOnly;
+ x.BaseRoutePath = zkOptions.BaseRoutePath;
+ x.LogToFile = zkOptions.LogToFile;
+ }
+
+ options.RegisterExtension(new ZookeeperOptionsExtension(configure));
+ return options;
+ }
+ }
+}
diff --git a/bus/EasyCaching.Bus.Zookeeper/Configurations/ZkBusOptions.cs b/bus/EasyCaching.Bus.Zookeeper/Configurations/ZkBusOptions.cs
new file mode 100644
index 00000000..5996a947
--- /dev/null
+++ b/bus/EasyCaching.Bus.Zookeeper/Configurations/ZkBusOptions.cs
@@ -0,0 +1,89 @@
+using System;
+
+namespace EasyCaching.Bus.Zookeeper
+{
+ public class ZkBusOptions
+ {
+ public ZkBusOptions()
+ {
+ this.ConnectionTimeout = 50000;//Milliseconds
+ this.OperatingTimeout = 10000;
+ this.SessionTimeout = 50000;
+ }
+
+ ///
+ /// create ZooKeeper client
+ ///
+ ///
+ ///
+ public ZkBusOptions(string connectionString)
+ {
+ if (string.IsNullOrEmpty(connectionString))
+ throw new ArgumentNullException(nameof(connectionString));
+
+ ConnectionString = connectionString;
+ }
+
+ ///
+ /// create ZooKeeper client
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public ZkBusOptions(string connectionString
+ , int connectionTimeout
+ , int operatingTimeout
+ , int sessionTimeout)
+ {
+ if (string.IsNullOrEmpty(connectionString))
+ throw new ArgumentNullException(nameof(connectionString));
+
+ ConnectionString = connectionString;
+ this.ConnectionTimeout = connectionTimeout;
+ this.SessionTimeout = sessionTimeout;
+ this.OperatingTimeout = operatingTimeout;
+ }
+
+ ///
+ /// connect string
+ ///
+ public string ConnectionString { get; set; }
+
+ ///
+ /// readonly
+ ///
+ public bool ReadOnly { get; set; } = false;
+
+ ///
+ /// point user to access
+ ///
+ public string Digest { get; set; }
+
+ ///
+ /// log to file options
+ ///
+ public bool LogToFile { get; set; } = false;
+
+ ///
+ /// base root path
+ ///
+ public string BaseRoutePath { get; set; } = "easyCacheBus";
+
+ ///
+ /// wait zooKeeper connect time with Milliseconds
+ ///
+ public int ConnectionTimeout { get; set; }
+
+ ///
+ /// execute zooKeeper handler retry waittime with Milliseconds
+ ///
+ public int OperatingTimeout { get; set; }
+
+ ///
+ /// zookeeper session timeout with Milliseconds
+ ///
+ public int SessionTimeout { get; set; }
+ }
+}
\ No newline at end of file
diff --git a/bus/EasyCaching.Bus.Zookeeper/Configurations/ZookeeperOptionsExtension.cs b/bus/EasyCaching.Bus.Zookeeper/Configurations/ZookeeperOptionsExtension.cs
new file mode 100644
index 00000000..901ff238
--- /dev/null
+++ b/bus/EasyCaching.Bus.Zookeeper/Configurations/ZookeeperOptionsExtension.cs
@@ -0,0 +1,36 @@
+namespace EasyCaching.Bus.Zookeeper
+{
+ using EasyCaching.Core.Bus;
+ using EasyCaching.Core.Configurations;
+ using EasyCaching.Core.Serialization;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.DependencyInjection.Extensions;
+ using System;
+
+ ///
+ /// Zookeeper options extension.
+ ///
+ internal sealed class ZookeeperOptionsExtension : IEasyCachingOptionsExtension
+ {
+ private readonly Action _zkBusOptions;
+
+ public ZookeeperOptionsExtension(Action zkBusOptions)
+ {
+ this._zkBusOptions = zkBusOptions;
+ }
+
+ ///
+ /// Adds the services.
+ ///
+ /// Services.
+ public void AddServices(IServiceCollection services)
+ {
+ services.AddOptions();
+
+ services.AddOptions()
+ .Configure(_zkBusOptions);
+
+ services.AddSingleton();
+ }
+ }
+}
\ No newline at end of file
diff --git a/bus/EasyCaching.Bus.Zookeeper/DefaultZookeeperBus.cs b/bus/EasyCaching.Bus.Zookeeper/DefaultZookeeperBus.cs
new file mode 100644
index 00000000..c507d008
--- /dev/null
+++ b/bus/EasyCaching.Bus.Zookeeper/DefaultZookeeperBus.cs
@@ -0,0 +1,376 @@
+namespace EasyCaching.Bus.Zookeeper
+{
+ using EasyCaching.Core;
+ using EasyCaching.Core.Bus;
+ using EasyCaching.Core.Serialization;
+ using Microsoft.Extensions.Options;
+ using org.apache.zookeeper;
+ using org.apache.zookeeper.data;
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Text;
+ using System.Threading;
+ using System.Threading.Tasks;
+
+ public class DefaultZookeeperBus : EasyCachingAbstractBus
+ {
+ ///
+ /// The zookeeper Bus options.
+ ///
+ private readonly ZkBusOptions _zkBusOptions;
+
+ ///
+ /// The zookeeper Client
+ ///
+ private ZooKeeper _zkClient;
+
+ ///
+ /// zookeeper data chane delegate event
+ ///
+ ///
+ ///
+
+ public delegate Task NodeDataChangeHandler(WatchedEvent @event);
+
+ ///
+ /// event
+ ///
+ private NodeDataChangeHandler _dataChangeHandler;
+
+ ///
+ /// lock
+ ///
+ private readonly object _zkEventLock = new object();
+
+ ///
+ /// The serializer.
+ ///
+ private readonly IEasyCachingSerializer _serializer;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ ///
+ public DefaultZookeeperBus(
+ IOptionsMonitor zkBusOptions
+ , IEasyCachingSerializer serializer)
+ {
+ this.BusName = "easycachingbus";
+ this._zkBusOptions = zkBusOptions.CurrentValue;
+ this._zkClient = CreateClient(zkBusOptions.CurrentValue, new ZkNodeDataWatch(this));
+
+ this._serializer = serializer;
+ }
+
+ ///
+ /// Publish the specified topic and message.
+ ///
+ /// Topic.
+ /// Message.
+ public override void BasePublish(string topic, EasyCachingMessage message)
+ {
+ var msg = _serializer.Serialize(message);
+ var path = $"{topic}";
+ Task.Run(async () =>
+ {
+ if (!await PathExistsAsync(path, true))
+ {
+ await CreateRecursiveAsync(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ }
+ await SetDataAsync(path, msg);
+ }).ConfigureAwait(false).GetAwaiter().GetResult();
+ }
+
+ ///
+ /// Publishs the async.
+ ///
+ /// The async.
+ /// Topic.
+ /// Message.
+ /// Cancellation token.
+ public override async Task BasePublishAsync(string topic, EasyCachingMessage message, CancellationToken cancellationToken = default(CancellationToken))
+ {
+ var msg = _serializer.Serialize(message);
+ var path = $"{topic}";
+
+ if (!await PathExistsAsync(path, true))
+ {
+ await CreateRecursiveAsync(path, null, ZooDefs.Ids.OPEN_ACL_UNSAFE, CreateMode.PERSISTENT);
+ }
+ await SetDataAsync(path, msg);
+ }
+
+ ///
+ /// Subscribe the specified topic and action.
+ ///
+ /// Topic.
+ /// Action.
+ public override void BaseSubscribe(string topic, Action action)
+ {
+ var path = $"{topic}";
+ Task.Factory.StartNew(async () =>
+ {
+ await SubscribeDataChangeAsync(path, SubscribeDataChange);
+ }, TaskCreationOptions.LongRunning);
+ }
+
+ ///
+ /// Ons the message.
+ ///
+ /// Body.
+ private void OnMessage(byte[] body)
+ {
+ var message = _serializer.Deserialize(body);
+ BaseOnMessage(message);
+ }
+
+ ///
+ /// create zk client
+ ///
+ ///
+ ///
+ ///
+ ///
+ private ZooKeeper CreateClient(ZkBusOptions options, Watcher watcher)
+ {
+ ZooKeeper.LogToFile = options.LogToFile;
+ var zk = new ZooKeeper(options.ConnectionString, options.SessionTimeout, watcher);
+ if (!string.IsNullOrEmpty(options.Digest))
+ {
+ zk.addAuthInfo("digest", Encoding.UTF8.GetBytes(options.Digest));
+ }
+ var operationStartTime = DateTime.Now;
+ while (true)
+ {
+ if (zk.getState() == ZooKeeper.States.CONNECTING)
+ {
+ Thread.Sleep(100);
+ }
+ else if (zk.getState() == ZooKeeper.States.CONNECTED
+ || zk.getState() == ZooKeeper.States.CONNECTEDREADONLY)
+ {
+ return zk;
+ }
+ if (DateTime.Now - operationStartTime > TimeSpan.FromMilliseconds(options.OperatingTimeout))
+ {
+ throw new TimeoutException(
+ $"connect cannot be retried because of retry timeout ({options.OperatingTimeout}Milliseconds)");
+ }
+ }
+ }
+
+ ///
+ /// subscribe data change
+ ///
+ ///
+ ///
+ private async Task SubscribeDataChange(WatchedEvent @event)
+ {
+ var state = @event.getState();
+ if (state == Watcher.Event.KeeperState.Expired)
+ {
+ await ReZkConnect();
+ }
+
+ var eventType = @event.get_Type();
+ byte[] nodeData = await GetDataAsync(@event.getPath());
+
+ switch (eventType)
+ {
+ case Watcher.Event.EventType.NodeCreated:
+ break;
+
+ case Watcher.Event.EventType.NodeDeleted:
+ case Watcher.Event.EventType.NodeDataChanged:
+ if (!nodeData.Any())
+ {
+ return;
+ }
+
+ //hander business logical
+ OnMessage(nodeData);
+ break;
+ }
+ await Task.CompletedTask;
+ }
+
+ ///
+ /// reconnnect zk
+ ///
+ ///
+ private async Task ReZkConnect()
+ {
+ if (!Monitor.TryEnter(_zkEventLock, _zkBusOptions.ConnectionTimeout))
+ return;
+ try
+ {
+ if (_zkClient != null)
+ {
+ try
+ {
+ await _zkClient.closeAsync();
+ }
+ catch
+ {
+ // ignored
+ }
+ }
+
+ _zkClient = CreateClient(_zkBusOptions, new ZkNodeDataWatch(this));
+ }
+ finally
+ {
+ Monitor.Exit(_zkEventLock);
+ }
+ }
+
+ ///
+ /// subscribe data change
+ ///
+ ///
+ ///
+ ///
+ private async Task SubscribeDataChangeAsync(string path, NodeDataChangeHandler listener)
+ {
+ _dataChangeHandler += listener;
+ await PathExistsAsync(path, true);
+ }
+
+ ///
+ /// pathExists
+ ///
+ ///
+ ///
+ ///
+ private async Task PathExistsAsync(string path, bool watch = false)
+ {
+ path = GetZooKeeperPath(path);
+ var state = await _zkClient.existsAsync(path, watch);
+ return state != null;
+ }
+
+ ///
+ /// set node data
+ ///
+ ///
+ ///
+ ///
+ /// node stat
+ public async Task SetDataAsync(string path, byte[] data, int version = -1)
+ {
+ path = GetZooKeeperPath(path);
+ var stat = await _zkClient.setDataAsync(path, data, version);
+ return stat;
+ }
+
+ ///
+ /// get data
+ ///
+ ///
+ ///
+ ///
+ public async Task GetDataAsync(string path, bool pathCv = false)
+ {
+ if (pathCv)
+ {
+ path = GetZooKeeperPath(path);
+ }
+ var data = await _zkClient.getDataAsync(path);
+ return data?.Data;
+ }
+
+ ///
+ /// recurive create
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ private async Task CreateRecursiveAsync(string path, byte[] data, List acls, CreateMode createMode)
+ {
+ path = GetZooKeeperPath(path);
+ var paths = path.Trim('/').Split('/');
+ var cur = "";
+ foreach (var item in paths)
+ {
+ if (string.IsNullOrEmpty(item))
+ {
+ continue;
+ }
+ cur += $"/{item}";
+ var existStat = await _zkClient.existsAsync(cur, null);
+ if (existStat != null)
+ {
+ continue;
+ }
+
+ if (cur.Equals(path))
+ {
+ await _zkClient.createAsync(cur, data, acls, createMode);
+ }
+ else
+ {
+ await _zkClient.createAsync(cur, null, acls, createMode);
+ }
+ }
+ return await Task.FromResult(true);
+ }
+
+ ///
+ ///
+ ///
+ ///
+ ///
+ private string GetZooKeeperPath(string path)
+ {
+ var basePath = _zkBusOptions.BaseRoutePath ?? "/";
+
+ if (!basePath.StartsWith("/"))
+ basePath = basePath.Insert(0, "/");
+
+ basePath = basePath.TrimEnd('/');
+
+ if (!path.StartsWith("/"))
+ path = path.Insert(0, "/");
+
+ path = $"{basePath}{path.TrimEnd('/')}";
+ return string.IsNullOrEmpty(path) ? "/" : path;
+ }
+
+ ///
+ /// watch zkNode data Change
+ ///
+ private class ZkNodeDataWatch : Watcher
+ {
+ private readonly DefaultZookeeperBus _defaultZookeeperBus;
+
+ public ZkNodeDataWatch(DefaultZookeeperBus defaultZookeeperBus)
+ {
+ _defaultZookeeperBus = defaultZookeeperBus;
+ }
+
+ public override async Task process(WatchedEvent watchedEvent)
+ {
+ var path = watchedEvent.getPath();
+ if (path != null)
+ {
+ var eventType = watchedEvent.get_Type();
+ var dataChanged = new[]
+ {
+ Watcher.Event.EventType.NodeCreated,
+ Watcher.Event.EventType.NodeDataChanged,
+ Watcher.Event.EventType.NodeDeleted
+ }.Contains(eventType);
+
+ if (dataChanged)
+ {
+ await _defaultZookeeperBus._dataChangeHandler(watchedEvent);
+ }
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/bus/EasyCaching.Bus.Zookeeper/EasyCaching.Bus.Zookeeper.csproj b/bus/EasyCaching.Bus.Zookeeper/EasyCaching.Bus.Zookeeper.csproj
new file mode 100644
index 00000000..5d754db8
--- /dev/null
+++ b/bus/EasyCaching.Bus.Zookeeper/EasyCaching.Bus.Zookeeper.csproj
@@ -0,0 +1,43 @@
+
+
+
+
+
+ netstandard2.0;net6.0
+ ncc;Catcher Wong
+ ncc;Catcher Wong
+ $(EasyCachingZookeeperBusPackageVersion)
+
+
+ A simple caching bus(message bus) based on Zookeeper.
+
+ Bus,Hybrid,Zookeeper,Caching,Cache
+ https://github.com/dotnetcore/EasyCaching
+ LICENSE
+ https://github.com/dotnetcore/EasyCaching
+ https://github.com/dotnetcore/EasyCaching
+ nuget-icon.png
+
+ $(EasyCachingZookeeperBusPackageNotes)
+
+
+
+
+ true
+ $(NoWarn);1591
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/sample/EasyCaching.Demo.Providers/Controllers/ValuesBusController.cs b/sample/EasyCaching.Demo.Providers/Controllers/ValuesBusController.cs
new file mode 100644
index 00000000..abb76ab8
--- /dev/null
+++ b/sample/EasyCaching.Demo.Providers/Controllers/ValuesBusController.cs
@@ -0,0 +1,46 @@
+namespace EasyCaching.Demo.Providers.Controllers
+{
+ using EasyCaching.Core;
+ using Microsoft.AspNetCore.Mvc;
+ using System;
+ using System.Threading.Tasks;
+
+ [Route("api/[controller]")]
+ public class ValuesBusController : Controller
+ {
+ //2. Hybird Cache
+ private readonly IHybridCachingProvider _provider;
+ private readonly IEasyCachingProviderFactory _factory;
+
+ public ValuesBusController(IHybridCachingProvider provider, IEasyCachingProviderFactory factory)
+ {
+ this._provider = provider;
+ _factory = factory;
+ }
+
+ // GET api/values
+ [HttpGet]
+ [Route("get2")]
+ public async Task Get2()
+ {
+ var rd = new Random(1000);
+ for (int i = 0; i < 5; i++)
+ {
+ var val = rd.Next().ToString();
+ await _provider.SetAsync($"demo{i}", val, TimeSpan.FromSeconds(5000));
+ var provider = _factory.GetCachingProvider("cus");
+ var v1 = provider.Get($"demow{i}");
+ //Console.WriteLine($"{i}-->{v1}");
+
+ await _provider.SetAsync($"demow{i}", $"changeda-{val}", TimeSpan.FromSeconds(5000));
+
+ //var v2 = provider.Get($"demo{i}");
+ //Console.WriteLine($"after--{i}-->{v2}");
+ //Console.WriteLine("------------------");
+ }
+ return $"hybrid";
+ }
+
+
+ }
+}
diff --git a/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj b/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
index f9afa091..0fb6da6f 100644
--- a/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
+++ b/sample/EasyCaching.Demo.Providers/EasyCaching.Demo.Providers.csproj
@@ -5,6 +5,8 @@
+
+
diff --git a/sample/EasyCaching.Demo.Providers/Program.cs b/sample/EasyCaching.Demo.Providers/Program.cs
index 81dd13f5..dbf64033 100644
--- a/sample/EasyCaching.Demo.Providers/Program.cs
+++ b/sample/EasyCaching.Demo.Providers/Program.cs
@@ -1,7 +1,9 @@
namespace EasyCaching.Demo.Providers
{
using Microsoft.AspNetCore.Hosting;
+ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Hosting;
+ using System.IO;
public class Program
{
@@ -12,6 +14,15 @@ public static void Main(string[] args)
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
+ .UseContentRoot(Directory.GetCurrentDirectory())
+ .ConfigureAppConfiguration((hosting, config) =>
+ {
+ config.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
+ .AddJsonFile($"appsettings.{hosting.HostingEnvironment.EnvironmentName}.json", optional: true,
+ true);
+
+ config.AddEnvironmentVariables();
+ })
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup();
diff --git a/sample/EasyCaching.Demo.Providers/Startup.cs b/sample/EasyCaching.Demo.Providers/Startup.cs
index 84160cc2..9f9e30f5 100644
--- a/sample/EasyCaching.Demo.Providers/Startup.cs
+++ b/sample/EasyCaching.Demo.Providers/Startup.cs
@@ -1,87 +1,87 @@
-namespace EasyCaching.Demo.Providers
+namespace EasyCaching.Demo.Providers
{
using EasyCaching.Core.Configurations;
using EasyCaching.SQLite;
- using Microsoft.AspNetCore.Builder;
- using Microsoft.AspNetCore.Hosting;
- using Microsoft.Extensions.Configuration;
- using Microsoft.Extensions.DependencyInjection;
- using Microsoft.Extensions.Hosting;
- using Microsoft.Extensions.Logging;
-
- public class Startup
- {
- public Startup(IConfiguration configuration)
- {
- Configuration = configuration;
- }
-
- public IConfiguration Configuration { get; }
-
- public void ConfigureServices(IServiceCollection services)
- {
- services.AddControllers();
-
- //new configuration
+ using Microsoft.AspNetCore.Builder;
+ using Microsoft.AspNetCore.Hosting;
+ using Microsoft.Extensions.Configuration;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.Hosting;
+ using Microsoft.Extensions.Logging;
+
+ public class Startup
+ {
+ public Startup(IConfiguration configuration)
+ {
+ Configuration = configuration;
+ }
+
+ public IConfiguration Configuration { get; }
+
+ public void ConfigureServices(IServiceCollection services)
+ {
+ services.AddControllers();
+
+ //new configuration
services.AddEasyCaching(option =>
- {
- //use memory cache
- option.UseInMemory("default");
-
- //use memory cache
- option.UseInMemory("cus");
-
- //use redis cache
+ {
+ //use memory cache
+ option.UseInMemory("default");
+
+ //use memory cache
+ option.UseInMemory("cus");
+
+ //use redis cache
option.UseRedis(config =>
- {
- config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
- config.DBConfig.SyncTimeout = 10000;
- config.DBConfig.AsyncTimeout = 10000;
- config.SerializerName = "mymsgpack";
- }, "redis1")
- .WithMessagePack("mymsgpack")//with messagepack serialization
- ;
-
- //use redis cache
+ {
+ config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6379));
+ config.DBConfig.SyncTimeout = 10000;
+ config.DBConfig.AsyncTimeout = 10000;
+ config.SerializerName = "mymsgpack";
+ }, "redis1")
+ .WithMessagePack("mymsgpack")//with messagepack serialization
+ ;
+
+ //use redis cache
option.UseRedis(config =>
- {
- config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6380));
- }, "redis2");
-
- //use sqlite cache
- option.UseSQLite(config =>
- {
- config.DBConfig = new SQLiteDBOptions { FileName = "my.db" };
- });
-
- //use memcached cached
+ {
+ config.DBConfig.Endpoints.Add(new ServerEndPoint("127.0.0.1", 6380));
+ }, "redis2");
+
+ //use sqlite cache
+ option.UseSQLite(config =>
+ {
+ config.DBConfig = new SQLiteDBOptions { FileName = "my.db" };
+ });
+
+ //use memcached cached
option.UseMemcached(config =>
- {
- config.DBConfig.AddServer("127.0.0.1", 11211);
- });
-
- option.UseMemcached(Configuration);
+ {
+ config.DBConfig.AddServer("127.0.0.1", 11211);
+ });
+
+ option.UseMemcached(Configuration);
});
- }
-
- public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
- {
- if (env.IsDevelopment())
- {
- app.UseDeveloperExceptionPage();
- }
-
- //loggerFactory.AddConsole(Configuration.GetSection("Logging"));
-
- // Important step for using Memcached Cache or SQLite Cache
- //app.UseEasyCaching();
-
- app.UseRouting();
-
- app.UseEndpoints(endpoints =>
- {
- endpoints.MapControllers();
- });
- }
- }
+ }
+
+ public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
+ {
+ if (env.IsDevelopment())
+ {
+ app.UseDeveloperExceptionPage();
+ }
+
+ //loggerFactory.AddConsole(Configuration.GetSection("Logging"));
+
+ // Important step for using Memcached Cache or SQLite Cache
+ //app.UseEasyCaching();
+
+ app.UseRouting();
+
+ app.UseEndpoints(endpoints =>
+ {
+ endpoints.MapControllers();
+ });
+ }
+ }
}
diff --git a/sample/EasyCaching.Demo.Providers/appsettings.json b/sample/EasyCaching.Demo.Providers/appsettings.json
index 8f4acb34..85b08a94 100644
--- a/sample/EasyCaching.Demo.Providers/appsettings.json
+++ b/sample/EasyCaching.Demo.Providers/appsettings.json
@@ -12,59 +12,70 @@
}
}
},
- "easycaching": {
- "inmemory": {
- "CachingProviderType": 1,
- "MaxRdSecond": 120,
- "Order": 2,
- },
- "sqlite": {
- "CachingProviderType": 3,
- "MaxRdSecond": 120,
- "Order": 2,
- "dbconfig": {
- "FileName": "my.db"
- }
- },
- "redis": {
- "CachingProviderType": 2,
- "MaxRdSecond": 120,
- "Order": 2,
- "dbconfig": {
- "Password": null,
- "IsSsl": false,
- "SslHost": null,
- "ConnectionTimeout": 5000,
- "AllowAdmin": true,
- "Endpoints": [
- {
- "Host": "localhost",
- "Port": 6739
- }
- ],
- "Database": 0
- }
- },
- "memcached": {
- "CachingProviderType": 4,
- "MaxRdSecond": 120,
- "Order": 2,
- "dbconfig": {
- "Servers": [
- {
- "Address": "127.0.0.1",
- "Port": 11211
- }
- ],
- "socketPool": {
- "minPoolSize": "5",
- "maxPoolSize": "25",
- "connectionTimeout": "00:00:15",
- "receiveTimeout": "00:00:15",
- "deadTimeout": "00:00:15",
- "queueTimeout": "00:00:00.150"
- }
- }
+ "easycaching": {
+ "inmemory": {
+ "CachingProviderType": 1,
+ "MaxRdSecond": 120,
+ "Order": 2
+ },
+ "sqlite": {
+ "CachingProviderType": 3,
+ "MaxRdSecond": 120,
+ "Order": 2,
+ "dbconfig": {
+ "FileName": "my.db"
+ }
+ },
+ "redis": {
+ "CachingProviderType": 2,
+ "MaxRdSecond": 120,
+ "Order": 2,
+ "dbconfig": {
+ "Password": null,
+ "IsSsl": false,
+ "SslHost": null,
+ "ConnectionTimeout": 5000,
+ "AllowAdmin": true,
+ "Endpoints": [
+ {
+ "Host": "127.0.0.1",
+ "Port": 6739
+ }
+ ],
+ "Database": 0
+ }
+ },
+ "memcached": {
+ "CachingProviderType": 4,
+ "MaxRdSecond": 120,
+ "Order": 2,
+ "dbconfig": {
+ "Servers": [
+ {
+ "Address": "127.0.0.1",
+ "Port": 11211
+ }
+ ],
+ "socketPool": {
+ "minPoolSize": "5",
+ "maxPoolSize": "25",
+ "connectionTimeout": "00:00:15",
+ "receiveTimeout": "00:00:15",
+ "deadTimeout": "00:00:15",
+ "queueTimeout": "00:00:00.150"
}
+ }
+ },
+ "kafkabus": {
+ // "BootstrapServers": "127.0.0.1:9093",
+ "ProducerConfig": {
+ "BootstrapServers": "127.0.0.1:9093"
+ },
+ "ConsumerConfig": {
+ "BootstrapServers": "127.0.0.1:9093",
+ "GroupId": "MyGroupId"
+ },
+ "ConsumerCount":2
}
+ }
}
diff --git a/src/EasyCaching.CSRedis/Configurations/RedisOptionsExtension.cs b/src/EasyCaching.CSRedis/Configurations/RedisOptionsExtension.cs
index 4b1b9230..0f2deaa1 100755
--- a/src/EasyCaching.CSRedis/Configurations/RedisOptionsExtension.cs
+++ b/src/EasyCaching.CSRedis/Configurations/RedisOptionsExtension.cs
@@ -48,8 +48,7 @@ public void AddServices(IServiceCollection services)
{
services.AddOptions();
- services.TryAddSingleton();
-
+
services.Configure(_name, _configure);
services.TryAddSingleton();
diff --git a/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.Async.cs b/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.Async.cs
index 501d0778..2135fb0f 100644
--- a/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.Async.cs
+++ b/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.Async.cs
@@ -299,6 +299,33 @@ public override async Task BaseRemoveByPrefixAsync(string prefix, CancellationTo
await Task.WhenAll(tasks);
}
+ ///
+ /// Removes the by pattern async.
+ ///
+ /// The by pattern async.
+ /// Pattern.
+ /// CancellationToken
+ public override async Task BaseRemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(pattern, nameof(pattern));
+
+ pattern = this.HandleKeyPattern(pattern);
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"RemoveByPatternAsync : pattern = {pattern}");
+
+ var redisKeys = this.SearchRedisKeys(pattern);
+
+ var tasks = new List>();
+
+ foreach (var item in redisKeys)
+ {
+ tasks.Add(_cache.DelAsync(item));
+ }
+
+ await Task.WhenAll(tasks);
+ }
+
///
/// Sets all async.
///
diff --git a/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.cs b/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.cs
index 5a3a8790..f335eb7e 100644
--- a/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.cs
+++ b/src/EasyCaching.CSRedis/DefaultCSRedisCachingProvider.cs
@@ -275,6 +275,22 @@ private string HandlePrefix(string prefix)
return prefix;
}
+
+ ///
+ /// Handles the pattern of CacheKey.
+ ///
+ /// Pattern of CacheKey.
+ private string HandleKeyPattern(string pattern)
+ {
+ // Forbid
+ if (pattern.Equals("*"))
+ throw new ArgumentException("the pattern should not equal to *");
+
+ if (!string.IsNullOrWhiteSpace(_cache.Nodes?.Values?.FirstOrDefault()?.Prefix))
+ pattern = _cache.Nodes?.Values?.FirstOrDefault()?.Prefix + pattern;
+
+ return pattern;
+ }
///
/// Searchs the redis keys.
@@ -401,6 +417,27 @@ public override void BaseRemoveByPrefix(string prefix)
}
}
+ ///
+ /// Remove cached value by pattern
+ ///
+ /// The pattern of cache key
+ public override void BaseRemoveByPattern(string pattern)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(pattern, nameof(pattern));
+
+ pattern = this.HandleKeyPattern(pattern);
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"RemoveByPattern : pattern = {pattern}");
+
+ var redisKeys = this.SearchRedisKeys(pattern);
+
+ foreach (var item in redisKeys)
+ {
+ _cache.Del(item);
+ }
+ }
+
///
/// Set the specified cacheKey, cacheValue and expiration.
///
diff --git a/src/EasyCaching.Core/Bus/EasyCachingMessage.cs b/src/EasyCaching.Core/Bus/EasyCachingMessage.cs
index 09bf6e1a..ab728a16 100644
--- a/src/EasyCaching.Core/Bus/EasyCachingMessage.cs
+++ b/src/EasyCaching.Core/Bus/EasyCachingMessage.cs
@@ -25,5 +25,11 @@ public class EasyCachingMessage
///
/// true if is prefix; otherwise, false.
public bool IsPrefix { get; set; }
+
+ ///
+ /// Gets or sets a value indicating whether this is pattern.
+ ///
+ /// true if is pattern; otherwise, false.
+ public bool IsPattern { get; set; }
}
}
diff --git a/src/EasyCaching.Core/EasyCachingAbstractProvider.cs b/src/EasyCaching.Core/EasyCachingAbstractProvider.cs
index 6f06028d..e7e348af 100644
--- a/src/EasyCaching.Core/EasyCachingAbstractProvider.cs
+++ b/src/EasyCaching.Core/EasyCachingAbstractProvider.cs
@@ -5,20 +5,20 @@ namespace EasyCaching.Core
using System;
using System.Collections.Generic;
using System.Diagnostics;
- using System.Linq;
- using System.Threading;
- using System.Threading.Tasks;
- using EasyCaching.Core.Configurations;
+ using System.Linq;
+ using System.Threading;
+ using System.Threading.Tasks;
+ using EasyCaching.Core.Configurations;
using EasyCaching.Core.Diagnostics;
public abstract class EasyCachingAbstractProvider : IEasyCachingProvider
{
protected static readonly DiagnosticListener s_diagnosticListener =
- new DiagnosticListener(EasyCachingDiagnosticListenerExtensions.DiagnosticListenerName);
-
- private readonly IDistributedLockFactory _lockFactory;
- private readonly BaseProviderOptions _options;
-
+ new DiagnosticListener(EasyCachingDiagnosticListenerExtensions.DiagnosticListenerName);
+
+ private readonly IDistributedLockFactory _lockFactory;
+ private readonly BaseProviderOptions _options;
+
protected string ProviderName { get; set; }
protected bool IsDistributedProvider { get; set; }
protected int ProviderMaxRdSecond { get; set; }
@@ -36,13 +36,13 @@ public abstract class EasyCachingAbstractProvider : IEasyCachingProvider
protected EasyCachingAbstractProvider() { }
- protected EasyCachingAbstractProvider(IDistributedLockFactory lockFactory, BaseProviderOptions options)
- {
- _lockFactory = lockFactory;
- _options = options;
- }
-
- public abstract object BaseGetDatabse();
+ protected EasyCachingAbstractProvider(IDistributedLockFactory lockFactory, BaseProviderOptions options)
+ {
+ _lockFactory = lockFactory;
+ _options = options;
+ }
+
+ public abstract object BaseGetDatabse();
public abstract bool BaseExists(string cacheKey);
public abstract Task BaseExistsAsync(string cacheKey, CancellationToken cancellationToken = default);
public abstract void BaseFlush();
@@ -64,6 +64,8 @@ protected EasyCachingAbstractProvider(IDistributedLockFactory lockFactory, BaseP
public abstract Task BaseRemoveAsync(string cacheKey, CancellationToken cancellationToken = default);
public abstract void BaseRemoveByPrefix(string prefix);
public abstract Task BaseRemoveByPrefixAsync(string prefix, CancellationToken cancellationToken = default);
+ public abstract void BaseRemoveByPattern(string pattern);
+ public abstract Task BaseRemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default);
public abstract void BaseSet(string cacheKey, T cacheValue, TimeSpan expiration);
public abstract void BaseSetAll(IDictionary values, TimeSpan expiration);
public abstract Task BaseSetAllAsync(IDictionary values, TimeSpan expiration, CancellationToken cancellationToken = default);
@@ -185,30 +187,30 @@ public CacheValue Get(string cacheKey, Func dataRetriever, TimeSpan exp
var operationId = s_diagnosticListener.WriteGetCacheBefore(new BeforeGetRequestEventData(CachingProviderType.ToString(), Name, nameof(Get), new[] { cacheKey }, expiration));
Exception e = null;
try
- {
+ {
if (_lockFactory == null) return BaseGet(cacheKey, dataRetriever, expiration);
var value = BaseGet(cacheKey);
- if (value.HasValue) return value;
-
- using (var @lock = _lockFactory.CreateLock(Name, $"{cacheKey}_Lock"))
- {
+ if (value.HasValue) return value;
+
+ using (var @lock = _lockFactory.CreateLock(Name, $"{cacheKey}_Lock"))
+ {
if (!@lock.Lock(_options.SleepMs)) throw new TimeoutException();
value = BaseGet(cacheKey);
- if (value.HasValue) return value;
-
- var item = dataRetriever();
- if (item != null || _options.CacheNulls)
- {
- BaseSet(cacheKey, item, expiration);
-
- return new CacheValue(item, true);
- }
- else
- {
- return CacheValue.NoValue;
- }
+ if (value.HasValue) return value;
+
+ var item = dataRetriever();
+ if (item != null || _options.CacheNulls)
+ {
+ BaseSet(cacheKey, item, expiration);
+
+ return new CacheValue(item, true);
+ }
+ else
+ {
+ return CacheValue.NoValue;
+ }
}
}
catch (Exception ex)
@@ -316,36 +318,36 @@ public async Task> GetAsync(string cacheKey, Func> data
if (_lockFactory == null) return await BaseGetAsync(cacheKey, dataRetriever, expiration, cancellationToken);
var value = await BaseGetAsync(cacheKey);
- if (value.HasValue) return value;
-
- var @lock = _lockFactory.CreateLock(Name, $"{cacheKey}_Lock");
- try
- {
+ if (value.HasValue) return value;
+
+ var @lock = _lockFactory.CreateLock(Name, $"{cacheKey}_Lock");
+ try
+ {
if (!await @lock.LockAsync(_options.SleepMs)) throw new TimeoutException();
value = await BaseGetAsync(cacheKey, cancellationToken);
- if (value.HasValue) return value;
-
- var task = dataRetriever();
- if (!task.IsCompleted &&
- await Task.WhenAny(task, Task.Delay(_options.LockMs)) != task)
- throw new TimeoutException();
-
- var item = await task;
- if (item != null || _options.CacheNulls)
- {
- await BaseSetAsync(cacheKey, item, expiration, cancellationToken);
-
- return new CacheValue(item, true);
- }
- else
- {
- return CacheValue.NoValue;
- }
- }
- finally
- {
- await @lock.DisposeAsync();
+ if (value.HasValue) return value;
+
+ var task = dataRetriever();
+ if (!task.IsCompleted &&
+ await Task.WhenAny(task, Task.Delay(_options.LockMs)) != task)
+ throw new TimeoutException();
+
+ var item = await task;
+ if (item != null || _options.CacheNulls)
+ {
+ await BaseSetAsync(cacheKey, item, expiration, cancellationToken);
+
+ return new CacheValue(item, true);
+ }
+ else
+ {
+ return CacheValue.NoValue;
+ }
+ }
+ finally
+ {
+ await @lock.DisposeAsync();
}
}
catch (Exception ex)
@@ -473,8 +475,8 @@ public async Task>> GetByPrefixAsync(string
public int GetCount(string prefix = "")
{
return BaseGetCount(prefix);
- }
-
+ }
+
public async Task GetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
{
return await BaseGetCountAsync(prefix, cancellationToken);
@@ -636,6 +638,62 @@ public async Task RemoveByPrefixAsync(string prefix, CancellationToken cancellat
}
}
+ public void RemoveByPattern(string pattern)
+ {
+ var operationId = s_diagnosticListener.WriteRemoveCacheBefore(
+ new BeforeRemoveRequestEventData(CachingProviderType.ToString(), Name, nameof(RemoveByPattern),
+ new[] { pattern }));
+ Exception e = null;
+ try
+ {
+ BaseRemoveByPattern(pattern);
+ }
+ catch (Exception ex)
+ {
+ e = ex;
+ throw;
+ }
+ finally
+ {
+ if (e != null)
+ {
+ s_diagnosticListener.WriteRemoveCacheError(operationId, e);
+ }
+ else
+ {
+ s_diagnosticListener.WriteRemoveCacheAfter(operationId);
+ }
+ }
+ }
+
+ public async Task RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ var operationId = s_diagnosticListener.WriteRemoveCacheBefore(
+ new BeforeRemoveRequestEventData(CachingProviderType.ToString(), Name, nameof(RemoveByPatternAsync),
+ new[] { pattern }));
+ Exception e = null;
+ try
+ {
+ await BaseRemoveByPatternAsync(pattern, cancellationToken);
+ }
+ catch (Exception ex)
+ {
+ e = ex;
+ throw;
+ }
+ finally
+ {
+ if (e != null)
+ {
+ s_diagnosticListener.WriteRemoveCacheError(operationId, e);
+ }
+ else
+ {
+ s_diagnosticListener.WriteRemoveCacheAfter(operationId);
+ }
+ }
+ }
+
public void Set(string cacheKey, T cacheValue, TimeSpan expiration)
{
var operationId = s_diagnosticListener.WriteSetCacheBefore(new BeforeSetRequestEventData(CachingProviderType.ToString(), Name, nameof(Set), new Dictionary { { cacheKey, cacheValue } }, expiration));
@@ -804,7 +862,37 @@ public async Task GetExpirationAsync(string cacheKey, CancellationToke
public ProviderInfo GetProviderInfo()
{
- return BaseGetProviderInfo();
+ return BaseGetProviderInfo();
+ }
+
+ protected SearchKeyPattern ProcessSearchKeyPattern(string pattern)
+ {
+ var postfix = pattern.StartsWith("*");
+ var prefix = pattern.EndsWith("*");
+
+ var contains = postfix && prefix;
+
+ if (contains)
+ {
+ return SearchKeyPattern.Contains;
+ }
+
+ if (postfix)
+ {
+ return SearchKeyPattern.Postfix;
+ }
+
+ if (prefix)
+ {
+ return SearchKeyPattern.Prefix;
+ }
+
+ return SearchKeyPattern.Exact;
}
+
+ protected string HandleSearchKeyPattern(string pattern)
+ {
+ return pattern.Replace("*", string.Empty);
+ }
}
}
diff --git a/src/EasyCaching.Core/IEasyCachingProviderBase.cs b/src/EasyCaching.Core/IEasyCachingProviderBase.cs
index 9b232e18..3c431f21 100644
--- a/src/EasyCaching.Core/IEasyCachingProviderBase.cs
+++ b/src/EasyCaching.Core/IEasyCachingProviderBase.cs
@@ -159,6 +159,19 @@ public interface IEasyCachingProviderBase
/// Prefix of CacheKey.
void RemoveByPrefix(string prefix);
+ ///
+ /// Removes cached items by a cache key pattern async.
+ ///
+ /// Pattern of CacheKeys.
+ ///
+ Task RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default);
+
+ ///
+ /// Removes cached items by a cache key pattern.
+ ///
+ /// Pattern of CacheKeys.
+ void RemoveByPattern(string pattern);
+
///
/// Removes cached item by cachekey's prefix async.
///
@@ -175,4 +188,4 @@ public interface IEasyCachingProviderBase
///
Task
-
+
diff --git a/src/EasyCaching.Redis/Configurations/RedisOptionsExtension.cs b/src/EasyCaching.Redis/Configurations/RedisOptionsExtension.cs
index dbe069ea..616142f8 100755
--- a/src/EasyCaching.Redis/Configurations/RedisOptionsExtension.cs
+++ b/src/EasyCaching.Redis/Configurations/RedisOptionsExtension.cs
@@ -44,8 +44,6 @@ public RedisOptionsExtension(string name, Action configure)
public void AddServices(IServiceCollection services)
{
services.AddOptions();
-
- services.TryAddSingleton();
services.Configure(_name, configure);
diff --git a/src/EasyCaching.Redis/DefaultRedisCachingProvider.Async.cs b/src/EasyCaching.Redis/DefaultRedisCachingProvider.Async.cs
index 3fffbfdb..8e967c75 100644
--- a/src/EasyCaching.Redis/DefaultRedisCachingProvider.Async.cs
+++ b/src/EasyCaching.Redis/DefaultRedisCachingProvider.Async.cs
@@ -229,6 +229,25 @@ public override async Task BaseRemoveByPrefixAsync(string prefix, CancellationTo
await _cache.KeyDeleteAsync(redisKeys);
}
+ ///
+ /// Removes cached item by pattern async.
+ ///
+ /// Pattern of CacheKey.
+ /// CancellationToken
+ public override async Task BaseRemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(pattern, nameof(pattern));
+
+ pattern = this.HandleKeyPattern(pattern);
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"RemoveByPatternAsync : pattern = {pattern}");
+
+ var redisKeys = this.SearchRedisKeys(pattern);
+
+ await _cache.KeyDeleteAsync(redisKeys);
+ }
+
///
/// Sets all async.
///
diff --git a/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs b/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs
index fcf9795c..f3836fb6 100644
--- a/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs
+++ b/src/EasyCaching.Redis/DefaultRedisCachingProvider.cs
@@ -229,6 +229,7 @@ public override void BaseRemove(string cacheKey)
_cache.KeyDelete(cacheKey);
}
+
///
/// Set the specified cacheKey, cacheValue and expiration.
@@ -285,6 +286,24 @@ public override void BaseRemoveByPrefix(string prefix)
_cache.KeyDelete(redisKeys);
}
+
+ ///
+ /// Removes cached item by pattern async.
+ ///
+ /// Pattern of CacheKey.
+ public override void BaseRemoveByPattern(string pattern)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(pattern, nameof(pattern));
+
+ pattern = this.HandleKeyPattern(pattern);
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"RemoveByPattern : pattern = {pattern}");
+
+ var redisKeys = this.SearchRedisKeys(pattern);
+
+ _cache.KeyDelete(redisKeys);
+ }
///
/// Searchs the redis keys.
@@ -356,7 +375,23 @@ private string HandlePrefix(string prefix)
return prefix;
}
+
+ ///
+ /// Handles the pattern of CacheKey.
+ ///
+ /// Pattern of CacheKey.
+ private string HandleKeyPattern(string pattern)
+ {
+ // Forbid
+ if (pattern.Equals("*"))
+ throw new ArgumentException("the pattern should not equal to *");
+
+ if (!string.IsNullOrWhiteSpace(_options.DBConfig.KeyPrefix))
+ pattern = _options.DBConfig.KeyPrefix + pattern;
+ return pattern;
+ }
+
///
/// Sets all.
///
diff --git a/src/EasyCaching.SQLite/Configurations/ConstSQL.cs b/src/EasyCaching.SQLite/Configurations/ConstSQL.cs
index d54bccf0..948160a1 100644
--- a/src/EasyCaching.SQLite/Configurations/ConstSQL.cs
+++ b/src/EasyCaching.SQLite/Configurations/ConstSQL.cs
@@ -69,9 +69,9 @@ FROM [easycaching]
public const string REMOVESQL = @"DELETE FROM [easycaching] WHERE [cachekey] = @cachekey AND [name] = @name ";
///
- /// The removebyprefixsql.
+ /// The removebylikesql.
///
- public const string REMOVEBYPREFIXSQL = @"DELETE FROM [easycaching] WHERE [cachekey] like @cachekey AND [name]=@name";
+ public const string REMOVEBYLIKESQL = @"DELETE FROM [easycaching] WHERE [cachekey] like @cachekey AND [name]=@name";
///
/// The existssql.
diff --git a/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.Async.cs b/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.Async.cs
index 6354aa87..c4973ceb 100644
--- a/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.Async.cs
+++ b/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.Async.cs
@@ -237,13 +237,32 @@ public override async Task BaseRemoveByPrefixAsync(string prefix, CancellationTo
if (_options.EnableLogging)
_logger?.LogInformation($"RemoveByPrefixAsync : prefix = {prefix}");
- await _cache.ExecuteAsync(new CommandDefinition(ConstSQL.REMOVEBYPREFIXSQL, new
+ await _cache.ExecuteAsync(new CommandDefinition(ConstSQL.REMOVEBYLIKESQL, new
{
cachekey = string.Concat(prefix, "%"),
name = _name
}, cancellationToken: cancellationToken));
}
+ ///
+ /// Removes cached item by pattern async.
+ ///
+ /// Pattern of CacheKey.
+ /// CancellationToken
+ public override async Task BaseRemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(pattern, nameof(pattern));
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"RemoveByPatternAsync : pattern = {pattern}");
+
+ await _cache.ExecuteAsync(new CommandDefinition(ConstSQL.REMOVEBYLIKESQL, new
+ {
+ cachekey = pattern.Replace('*', '%'),
+ name = _name
+ }, cancellationToken: cancellationToken));
+ }
+
///
/// Sets all async.
///
diff --git a/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs b/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs
index 7610a3f2..47c178d1 100644
--- a/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs
+++ b/src/EasyCaching.SQLite/DefaultSQLiteCachingProvider.cs
@@ -234,9 +234,23 @@ public override void BaseRemoveByPrefix(string prefix)
if (_options.EnableLogging)
_logger?.LogInformation($"RemoveByPrefix : prefix = {prefix}");
- _cache.Execute(ConstSQL.REMOVEBYPREFIXSQL, new { cachekey = string.Concat(prefix, "%"), name = _name });
+ _cache.Execute(ConstSQL.REMOVEBYLIKESQL, new { cachekey = string.Concat(prefix, "%"), name = _name });
}
+ ///
+ /// Removes cached item by pattern async.
+ ///
+ /// Pattern of CacheKey.
+ public override void BaseRemoveByPattern(string pattern)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(pattern, nameof(pattern));
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"RemoveByPattern : pattern = {pattern}");
+
+ _cache.Execute(ConstSQL.REMOVEBYLIKESQL, new { cachekey = pattern.Replace('*', '%'), name = _name });
+ }
+
///
/// Sets all.
///
diff --git a/test/EasyCaching.PerformanceTests/SerializerBenchmark.cs b/test/EasyCaching.PerformanceTests/SerializerBenchmark.cs
index e1d2a2bd..158ba8da 100644
--- a/test/EasyCaching.PerformanceTests/SerializerBenchmark.cs
+++ b/test/EasyCaching.PerformanceTests/SerializerBenchmark.cs
@@ -18,7 +18,6 @@ public abstract class SerializerBenchmark
private DefaultJsonSerializer _json = new DefaultJsonSerializer("json", new JsonSerializerSettings());
private DefaultMessagePackSerializer _messagepack = new DefaultMessagePackSerializer("msgpack", new EasyCachingMsgPackSerializerOptions { });
private DefaultProtobufSerializer _protobuf = new DefaultProtobufSerializer("proto");
- private DefaultBinaryFormatterSerializer _binary = new DefaultBinaryFormatterSerializer();
protected MyPoco _single;
protected List _list;
private int _count;
@@ -39,12 +38,6 @@ public void Setup()
_list = items;
}
- [Benchmark]
- public void BinaryFormatter()
- {
- Exec(_binary);
- }
-
[Benchmark]
public void Json()
{
diff --git a/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs b/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs
index b937e41c..ce00bdf9 100644
--- a/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs
+++ b/test/EasyCaching.UnitTests/CachingTests/BaseCachingProviderTest.cs
@@ -868,6 +868,132 @@ protected virtual async Task RemoveByPrefixAsync_Should_Succeed()
Assert.False(demo4.HasValue);
Assert.True(xxx1.HasValue);
}
+
+ #endregion
+
+ #region RemoveByPattern/RemoveByPatternAsync
+
+ [Fact]
+ public virtual void RemoveByPattern_Should_Succeed()
+ {
+ SetCacheItem("garden:pots:flowers", "ok");
+ SetCacheItem("garden:pots:flowers:test", "ok");
+ SetCacheItem("garden:flowerspots:test", "ok" );
+ SetCacheItem("boo:foo", "ok");
+ SetCacheItem("boo:test:foo", "ok");
+ SetCacheItem("sky:birds:bar", "ok");
+ SetCacheItem("sky:birds:test:bar", "ok");
+ SetCacheItem("akey", "ok");
+
+ var val1 = _provider.Get("garden:pots:flowers");
+ var val2 = _provider.Get("garden:pots:flowers:test");
+ var val3 = _provider.Get("garden:flowerspots:test");
+ var val4 = _provider.Get("boo:foo");
+ var val5 = _provider.Get("boo:test:foo");
+ var val6 = _provider.Get("sky:birds:bar");
+ var val7 = _provider.Get("sky:birds:test:bar");
+ var val8 = _provider.Get("akey");
+
+ Assert.True(val1.HasValue);
+ Assert.True(val2.HasValue);
+ Assert.True(val3.HasValue);
+ Assert.True(val4.HasValue);
+ Assert.True(val5.HasValue);
+ Assert.True(val6.HasValue);
+ Assert.True(val7.HasValue);
+ Assert.True(val8.HasValue);
+
+ // contains
+ _provider.RemoveByPattern("*:pots:*");
+
+ // postfix
+ _provider.RemoveByPattern("*foo");
+
+ // prefix
+ _provider.RemoveByPattern("sky*");
+
+ // exact
+ _provider.RemoveByPattern("akey");
+
+ var val9 = _provider.Get("garden:pots:flowers");
+ var val10 = _provider.Get("garden:pots:flowers:test");
+ var val11 = _provider.Get("garden:flowerspots:test");
+ var val12 = _provider.Get("boo:foo");
+ var val13 = _provider.Get("boo:test:foo");
+ var val14 = _provider.Get("sky:birds:bar");
+ var val15 = _provider.Get("sky:birds:test:bar");
+ var val16 = _provider.Get("akey");
+
+ Assert.False(val9.HasValue);
+ Assert.False(val10.HasValue);
+ Assert.True(val11.HasValue);
+ Assert.False(val12.HasValue);
+ Assert.False(val13.HasValue);
+ Assert.False(val14.HasValue);
+ Assert.False(val15.HasValue);
+ Assert.False(val16.HasValue);
+ }
+
+ [Fact]
+ public virtual async Task RemoveByPatternAsync_Should_Succeed()
+ {
+ SetCacheItem("garden:pots:flowers", "ok");
+ SetCacheItem("garden:pots:flowers:test", "ok");
+ SetCacheItem("garden:flowerspots:test", "ok" );
+ SetCacheItem("boo:foo", "ok");
+ SetCacheItem("boo:test:foo", "ok");
+ SetCacheItem("sky:birds:bar", "ok");
+ SetCacheItem("sky:birds:test:bar", "ok");
+ SetCacheItem("akey", "ok");
+
+ var val1 = _provider.Get("garden:pots:flowers");
+ var val2 = _provider.Get("garden:pots:flowers:test");
+ var val3 = _provider.Get("garden:flowerspots:test");
+ var val4 = _provider.Get("boo:foo");
+ var val5 = _provider.Get("boo:test:foo");
+ var val6 = _provider.Get("sky:birds:bar");
+ var val7 = _provider.Get("sky:birds:test:bar");
+ var val8 = _provider.Get("akey");
+
+ Assert.True(val1.HasValue);
+ Assert.True(val2.HasValue);
+ Assert.True(val3.HasValue);
+ Assert.True(val4.HasValue);
+ Assert.True(val5.HasValue);
+ Assert.True(val6.HasValue);
+ Assert.True(val7.HasValue);
+ Assert.True(val8.HasValue);
+
+ // contains
+ await _provider.RemoveByPatternAsync("*:pots:*");
+
+ // postfix
+ await _provider.RemoveByPatternAsync("*foo");
+
+ // prefix
+ await _provider.RemoveByPatternAsync("sky*");
+
+ // exact
+ await _provider.RemoveByPatternAsync("akey");
+
+ var val9 = _provider.Get("garden:pots:flowers");
+ var val10 = _provider.Get("garden:pots:flowers:test");
+ var val11 = _provider.Get("garden:flowerspots:test");
+ var val12 = _provider.Get("boo:foo");
+ var val13 = _provider.Get("boo:test:foo");
+ var val14 = _provider.Get("sky:birds:bar");
+ var val15 = _provider.Get("sky:birds:test:bar");
+ var val16 = _provider.Get("akey");
+
+ Assert.False(val9.HasValue);
+ Assert.False(val10.HasValue);
+ Assert.True(val11.HasValue);
+ Assert.False(val12.HasValue);
+ Assert.False(val13.HasValue);
+ Assert.False(val14.HasValue);
+ Assert.False(val15.HasValue);
+ Assert.False(val16.HasValue);
+ }
#endregion
#region SetAll/SetAllAsync
@@ -1426,4 +1552,4 @@ protected virtual void OnHit_Should_Return_Zero_And_OnMiss_Should_Return_One()
Assert.Equal(1, missedRes);
}
}
-}
\ No newline at end of file
+}
diff --git a/test/EasyCaching.UnitTests/CachingTests/CSRedisCachingProviderTest.cs b/test/EasyCaching.UnitTests/CachingTests/CSRedisCachingProviderTest.cs
index 8e132070..c90a2343 100644
--- a/test/EasyCaching.UnitTests/CachingTests/CSRedisCachingProviderTest.cs
+++ b/test/EasyCaching.UnitTests/CachingTests/CSRedisCachingProviderTest.cs
@@ -1,3 +1,5 @@
+using System.Threading.Tasks;
+
namespace EasyCaching.UnitTests
{
using System;
@@ -146,8 +148,7 @@ public CSRedisCachingProviderWithKeyPrefixTest()
config.SerializerName = "json";
}, "WithKeyPrefix");
-
-
+
x.WithJson("json");
});
@@ -193,5 +194,135 @@ public void RemoveByPrefixTest()
Assert.False(val3.HasValue);
Assert.False(val4.HasValue);
}
+
+ [Theory]
+ [InlineData("WithKeyPrefix")]
+ [InlineData("NotKeyPrefix")]
+ public void RemoveByKeyPatternTest(string provider)
+ {
+ var WithKeyPrefix = _providerFactory.GetCachingProvider(provider);
+
+ WithKeyPrefix.Set("garden:pots:flowers", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("garden:pots:flowers:test", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("garden:flowerspots:test", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("boo:foo", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("boo:test:foo", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("sky:birds:bar", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("sky:birds:test:bar", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("akey", "ok", TimeSpan.FromSeconds(10));
+
+ var val1 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val2 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val3 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val4 = WithKeyPrefix.Get("boo:foo");
+ var val5 = WithKeyPrefix.Get("boo:test:foo");
+ var val6 = WithKeyPrefix.Get("sky:birds:bar");
+ var val7 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val8 = WithKeyPrefix.Get("akey");
+
+ Assert.True(val1.HasValue);
+ Assert.True(val2.HasValue);
+ Assert.True(val3.HasValue);
+ Assert.True(val4.HasValue);
+ Assert.True(val5.HasValue);
+ Assert.True(val6.HasValue);
+ Assert.True(val7.HasValue);
+ Assert.True(val8.HasValue);
+
+ // contains
+ WithKeyPrefix.RemoveByPattern("*:pots:*");
+
+ // postfix
+ WithKeyPrefix.RemoveByPattern("*foo");
+
+ // prefix
+ WithKeyPrefix.RemoveByPattern("sky*");
+
+ // exact
+ WithKeyPrefix.RemoveByPattern("akey");
+
+ var val9 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val10 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val11 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val12 = WithKeyPrefix.Get("boo:foo");
+ var val13 = WithKeyPrefix.Get("boo:test:foo");
+ var val14 = WithKeyPrefix.Get("sky:birds:bar");
+ var val15 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val16 = WithKeyPrefix.Get("akey");
+
+ Assert.False(val9.HasValue);
+ Assert.False(val10.HasValue);
+ Assert.True(val11.HasValue);
+ Assert.False(val12.HasValue);
+ Assert.False(val13.HasValue);
+ Assert.False(val14.HasValue);
+ Assert.False(val15.HasValue);
+ Assert.False(val16.HasValue);
+ }
+
+ [Theory]
+ [InlineData("WithKeyPrefix")]
+ [InlineData("NotKeyPrefix")]
+ public async Task RemoveByKeyPatternAsyncTest(string provider)
+ {
+ var WithKeyPrefix = _providerFactory.GetCachingProvider(provider);
+
+ await WithKeyPrefix.SetAsync("garden:pots:flowers", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("garden:pots:flowers:test", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("garden:flowerspots:test", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("boo:foo", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("boo:test:foo", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("sky:birds:bar", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("sky:birds:test:bar", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("akey", "ok", TimeSpan.FromSeconds(10));
+
+ var val1 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val2 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val3 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val4 = WithKeyPrefix.Get("boo:foo");
+ var val5 = WithKeyPrefix.Get("boo:test:foo");
+ var val6 = WithKeyPrefix.Get("sky:birds:bar");
+ var val7 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val8 = WithKeyPrefix.Get("akey");
+
+ Assert.True(val1.HasValue);
+ Assert.True(val2.HasValue);
+ Assert.True(val3.HasValue);
+ Assert.True(val4.HasValue);
+ Assert.True(val5.HasValue);
+ Assert.True(val6.HasValue);
+ Assert.True(val7.HasValue);
+ Assert.True(val8.HasValue);
+
+ // contains
+ await WithKeyPrefix.RemoveByPatternAsync("*:pots:*");
+
+ // postfix
+ await WithKeyPrefix.RemoveByPatternAsync("*foo");
+
+ // prefix
+ await WithKeyPrefix.RemoveByPatternAsync("sky*");
+
+ // exact
+ await WithKeyPrefix.RemoveByPatternAsync("akey");
+
+ var val9 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val10 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val11 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val12 = WithKeyPrefix.Get("boo:foo");
+ var val13 = WithKeyPrefix.Get("boo:test:foo");
+ var val14 = WithKeyPrefix.Get("sky:birds:bar");
+ var val15 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val16 = WithKeyPrefix.Get("akey");
+
+ Assert.False(val9.HasValue);
+ Assert.False(val10.HasValue);
+ Assert.True(val11.HasValue);
+ Assert.False(val12.HasValue);
+ Assert.False(val13.HasValue);
+ Assert.False(val14.HasValue);
+ Assert.False(val15.HasValue);
+ Assert.False(val16.HasValue);
+ }
}
}
diff --git a/test/EasyCaching.UnitTests/CachingTests/MemcachedProviderTest.cs b/test/EasyCaching.UnitTests/CachingTests/MemcachedProviderTest.cs
index 035efdb3..0d5cf30a 100644
--- a/test/EasyCaching.UnitTests/CachingTests/MemcachedProviderTest.cs
+++ b/test/EasyCaching.UnitTests/CachingTests/MemcachedProviderTest.cs
@@ -24,8 +24,9 @@ protected override IEasyCachingProvider CreateCachingProvider(Action
{
options.DBConfig.AddServer("127.0.0.1", 11211);
+ options.SerializerName = "msg";
additionalSetup(options);
- }).UseMemcachedLock());
+ }).WithMessagePack("msg").UseMemcachedLock());
services.AddLogging();
IServiceProvider serviceProvider = services.BuildServiceProvider();
return serviceProvider.GetService();
@@ -114,6 +115,17 @@ protected override void GetByPrefix_Should_Succeed()
{
}
+ [Fact]
+ public override void RemoveByPattern_Should_Succeed()
+ {
+ }
+
+ [Fact]
+ public override async Task RemoveByPatternAsync_Should_Succeed()
+ {
+ await Task.FromResult(1);
+ }
+
[Fact]
protected override async Task GetByPrefixAsync_Should_Succeed()
{
@@ -212,9 +224,18 @@ public MemcachedProviderWithFactoryTest()
IServiceCollection services = new ServiceCollection();
services.AddEasyCaching(x =>
{
- x.UseMemcached(options => { options.DBConfig.AddServer("127.0.0.1", 11212); }, SECOND_PROVIDER_NAME)
+ x.WithMessagePack("msg");
+ x.UseMemcached(options =>
+ {
+ options.DBConfig.AddServer("127.0.0.1", 11212);
+ options.SerializerName = "msg";
+ }, SECOND_PROVIDER_NAME)
.UseMemcached(
- options => { options.DBConfig.AddServer("127.0.0.1", 11211); }, "MyTest");
+ options =>
+ {
+ options.DBConfig.AddServer("127.0.0.1", 11211);
+ options.SerializerName = "msg";
+ }, "MyTest");
});
services.AddLogging();
IServiceProvider serviceProvider = services.BuildServiceProvider();
@@ -335,6 +356,7 @@ public MemcachedProviderUseEasyCachingTest()
services.AddLogging();
services.AddEasyCaching(option =>
{
+ option.WithMessagePack("msg");
option.UseMemcached(config =>
{
config.DBConfig = new EasyCachingMemcachedClientOptions
@@ -344,6 +366,7 @@ public MemcachedProviderUseEasyCachingTest()
new Enyim.Caching.Configuration.Server() {Address = "127.0.0.1", Port = 11212}
}
};
+ config.SerializerName = "msg";
}, EasyCachingConstValue.DefaultMemcachedName);
});
@@ -361,7 +384,7 @@ public MemcachedProviderUseEasyCachingWithConfigTest()
{
IServiceCollection services = new ServiceCollection();
- var appsettings = " { \"easycaching\": { \"memcached\": { \"MaxRdSecond\": 600, \"dbconfig\": { \"Servers\": [ { \"Address\": \"127.0.0.1\", \"Port\": 11211 } ] } } }}";
+ var appsettings = " {\"easycaching\":{\"memcached\":{\"MaxRdSecond\":600,\"dbconfig\":{\"Servers\":[{\"Address\":\"127.0.0.1\",\"Port\":11211}]},\"SerializerName\":\"msg\"}}} ";
var path = TestHelpers.CreateTempFile(appsettings);
var directory = Path.GetDirectoryName(path);
var fileName = Path.GetFileName(path);
@@ -371,7 +394,11 @@ public MemcachedProviderUseEasyCachingWithConfigTest()
configurationBuilder.AddJsonFile(fileName);
var config = configurationBuilder.Build();
services.AddLogging();
- services.AddEasyCaching(option => { option.UseMemcached(config, "mName"); });
+ services.AddEasyCaching(option =>
+ {
+ option.WithMessagePack("msg");
+ option.UseMemcached(config, "mName");
+ });
IServiceProvider serviceProvider = services.BuildServiceProvider();
_provider = serviceProvider.GetService();
@@ -396,6 +423,7 @@ public async void NoConnectionTest()
services.AddLogging();
services.AddEasyCaching(option =>
{
+ option.WithMessagePack("msg");
option.UseMemcached(config =>
{
config.DBConfig = new EasyCachingMemcachedClientOptions
@@ -411,6 +439,7 @@ public async void NoConnectionTest()
ReceiveTimeout = TimeSpan.FromSeconds(2),
}
};
+ config.SerializerName = "msg";
}, EasyCachingConstValue.DefaultMemcachedName);
});
@@ -424,4 +453,4 @@ public async void NoConnectionTest()
await Assert.ThrowsAnyAsync(() => provider.RemoveAsync("123123"));
}
}
-}
\ No newline at end of file
+}
diff --git a/test/EasyCaching.UnitTests/CachingTests/RedisCachingProviderTest.cs b/test/EasyCaching.UnitTests/CachingTests/RedisCachingProviderTest.cs
index 1cb92953..28463ed2 100644
--- a/test/EasyCaching.UnitTests/CachingTests/RedisCachingProviderTest.cs
+++ b/test/EasyCaching.UnitTests/CachingTests/RedisCachingProviderTest.cs
@@ -1,3 +1,5 @@
+using System.Threading.Tasks;
+
namespace EasyCaching.UnitTests
{
using EasyCaching.Core;
@@ -286,5 +288,135 @@ public void RemoveByPrefixTest()
Assert.False(val3.HasValue);
Assert.False(val4.HasValue);
}
+
+ [Theory]
+ [InlineData("WithKeyPrefix")]
+ [InlineData("NotKeyPrefix")]
+ public void RemoveByKeyPatternTest(string provider)
+ {
+ var WithKeyPrefix = _providerFactory.GetCachingProvider(provider);
+
+ WithKeyPrefix.Set("garden:pots:flowers", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("garden:pots:flowers:test", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("garden:flowerspots:test", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("boo:foo", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("boo:test:foo", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("sky:birds:bar", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("sky:birds:test:bar", "ok", TimeSpan.FromSeconds(10));
+ WithKeyPrefix.Set("akey", "ok", TimeSpan.FromSeconds(10));
+
+ var val1 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val2 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val3 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val4 = WithKeyPrefix.Get("boo:foo");
+ var val5 = WithKeyPrefix.Get("boo:test:foo");
+ var val6 = WithKeyPrefix.Get("sky:birds:bar");
+ var val7 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val8 = WithKeyPrefix.Get("akey");
+
+ Assert.True(val1.HasValue);
+ Assert.True(val2.HasValue);
+ Assert.True(val3.HasValue);
+ Assert.True(val4.HasValue);
+ Assert.True(val5.HasValue);
+ Assert.True(val6.HasValue);
+ Assert.True(val7.HasValue);
+ Assert.True(val8.HasValue);
+
+ // contains
+ WithKeyPrefix.RemoveByPattern("*:pots:*");
+
+ // postfix
+ WithKeyPrefix.RemoveByPattern("*foo");
+
+ // prefix
+ WithKeyPrefix.RemoveByPattern("sky*");
+
+ // exact
+ WithKeyPrefix.RemoveByPattern("akey");
+
+ var val9 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val10 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val11 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val12 = WithKeyPrefix.Get("boo:foo");
+ var val13 = WithKeyPrefix.Get("boo:test:foo");
+ var val14 = WithKeyPrefix.Get("sky:birds:bar");
+ var val15 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val16 = WithKeyPrefix.Get("akey");
+
+ Assert.False(val9.HasValue);
+ Assert.False(val10.HasValue);
+ Assert.True(val11.HasValue);
+ Assert.False(val12.HasValue);
+ Assert.False(val13.HasValue);
+ Assert.False(val14.HasValue);
+ Assert.False(val15.HasValue);
+ Assert.False(val16.HasValue);
+ }
+
+ [Theory]
+ [InlineData("WithKeyPrefix")]
+ [InlineData("NotKeyPrefix")]
+ public async Task RemoveByKeyPatternAsyncTest(string provider)
+ {
+ var WithKeyPrefix = _providerFactory.GetCachingProvider(provider);
+
+ await WithKeyPrefix.SetAsync("garden:pots:flowers", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("garden:pots:flowers:test", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("garden:flowerspots:test", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("boo:foo", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("boo:test:foo", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("sky:birds:bar", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("sky:birds:test:bar", "ok", TimeSpan.FromSeconds(10));
+ await WithKeyPrefix.SetAsync("akey", "ok", TimeSpan.FromSeconds(10));
+
+ var val1 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val2 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val3 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val4 = WithKeyPrefix.Get("boo:foo");
+ var val5 = WithKeyPrefix.Get("boo:test:foo");
+ var val6 = WithKeyPrefix.Get("sky:birds:bar");
+ var val7 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val8 = WithKeyPrefix.Get("akey");
+
+ Assert.True(val1.HasValue);
+ Assert.True(val2.HasValue);
+ Assert.True(val3.HasValue);
+ Assert.True(val4.HasValue);
+ Assert.True(val5.HasValue);
+ Assert.True(val6.HasValue);
+ Assert.True(val7.HasValue);
+ Assert.True(val8.HasValue);
+
+ // contains
+ await WithKeyPrefix.RemoveByPatternAsync("*:pots:*");
+
+ // postfix
+ await WithKeyPrefix.RemoveByPatternAsync("*foo");
+
+ // prefix
+ await WithKeyPrefix.RemoveByPatternAsync("sky*");
+
+ // exact
+ await WithKeyPrefix.RemoveByPatternAsync("akey");
+
+ var val9 = WithKeyPrefix.Get("garden:pots:flowers");
+ var val10 = WithKeyPrefix.Get("garden:pots:flowers:test");
+ var val11 = WithKeyPrefix.Get("garden:flowerspots:test");
+ var val12 = WithKeyPrefix.Get("boo:foo");
+ var val13 = WithKeyPrefix.Get("boo:test:foo");
+ var val14 = WithKeyPrefix.Get("sky:birds:bar");
+ var val15 = WithKeyPrefix.Get("sky:birds:test:bar");
+ var val16 = WithKeyPrefix.Get("akey");
+
+ Assert.False(val9.HasValue);
+ Assert.False(val10.HasValue);
+ Assert.True(val11.HasValue);
+ Assert.False(val12.HasValue);
+ Assert.False(val13.HasValue);
+ Assert.False(val14.HasValue);
+ Assert.False(val15.HasValue);
+ Assert.False(val16.HasValue);
+ }
}
-}
\ No newline at end of file
+}
diff --git a/test/EasyCaching.UnitTests/Diagnostics/MyCachingProvider.cs b/test/EasyCaching.UnitTests/Diagnostics/MyCachingProvider.cs
index 698d10f7..84556c6c 100644
--- a/test/EasyCaching.UnitTests/Diagnostics/MyCachingProvider.cs
+++ b/test/EasyCaching.UnitTests/Diagnostics/MyCachingProvider.cs
@@ -2,8 +2,8 @@
{
using EasyCaching.Core;
using System;
- using System.Collections.Generic;
- using System.Threading;
+ using System.Collections.Generic;
+ using System.Threading;
using System.Threading.Tasks;
public class MyCachingProvider : EasyCachingAbstractProvider
@@ -85,13 +85,13 @@ public override Task>> BaseGetByPrefixAsync
public override int BaseGetCount(string prefix = "")
{
return 1;
- }
-
- public override Task BaseGetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
- {
- return Task.FromResult(1);
- }
-
+ }
+
+ public override Task BaseGetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(1);
+ }
+
public override TimeSpan BaseGetExpiration(string cacheKey)
{
return TimeSpan.FromSeconds(1);
@@ -137,6 +137,15 @@ public override Task BaseRemoveByPrefixAsync(string prefix, CancellationToken ca
return Task.CompletedTask;
}
+ public override void BaseRemoveByPattern(string pattern)
+ {
+ }
+
+ public override Task BaseRemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ return Task.CompletedTask;
+ }
+
public override void BaseSet(string cacheKey, T cacheValue, TimeSpan expiration)
{
diff --git a/test/EasyCaching.UnitTests/DistributedLock/MemcachedLockTest.cs b/test/EasyCaching.UnitTests/DistributedLock/MemcachedLockTest.cs
index 75e56385..4bc9b589 100644
--- a/test/EasyCaching.UnitTests/DistributedLock/MemcachedLockTest.cs
+++ b/test/EasyCaching.UnitTests/DistributedLock/MemcachedLockTest.cs
@@ -12,8 +12,12 @@ public class MemcachedLockTest : DistributedLockTest
{
private static readonly IDistributedLockFactory Factory = new ServiceCollection()
.AddLogging()
- .AddEasyCaching(option => option.UseMemcached(config =>
+ .AddEasyCaching(option =>
+ {
+ option.WithMessagePack("msg");
+ option.UseMemcached(config =>
{
+ config.SerializerName = "msg";
config.DBConfig = new EasyCachingMemcachedClientOptions
{
Servers =
@@ -21,8 +25,9 @@ public class MemcachedLockTest : DistributedLockTest
new Server { Address = "127.0.0.1", Port = 11211 }
}
};
- })
- .UseMemcachedLock())
+ });
+ option.UseMemcachedLock();
+ })
.BuildServiceProvider()
.GetService();
diff --git a/test/EasyCaching.UnitTests/Fake/FakeDistributedCachingProvider.cs b/test/EasyCaching.UnitTests/Fake/FakeDistributedCachingProvider.cs
index 8d350618..a059593f 100644
--- a/test/EasyCaching.UnitTests/Fake/FakeDistributedCachingProvider.cs
+++ b/test/EasyCaching.UnitTests/Fake/FakeDistributedCachingProvider.cs
@@ -1,8 +1,8 @@
namespace EasyCaching.UnitTests
{
using System;
- using System.Collections.Generic;
- using System.Threading;
+ using System.Collections.Generic;
+ using System.Threading;
using System.Threading.Tasks;
using EasyCaching.Core;
@@ -94,13 +94,13 @@ public virtual Task>> GetByPrefixAsync(stri
public virtual int GetCount(string prefix = "")
{
return 1;
- }
-
- public Task GetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
- {
- return Task.FromResult(1);
- }
-
+ }
+
+ public Task GetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(1);
+ }
+
public virtual TimeSpan GetExpiration(string cacheKey)
{
return TimeSpan.FromSeconds(1);
@@ -151,6 +151,15 @@ public virtual void RemoveByPrefix(string prefix)
}
+ public Task RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ return Task.CompletedTask;
+ }
+
+ public void RemoveByPattern(string pattern)
+ {
+ }
+
public virtual Task RemoveByPrefixAsync(string prefix, CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
diff --git a/test/EasyCaching.UnitTests/Fake/FakeLocalCachingProvider.cs b/test/EasyCaching.UnitTests/Fake/FakeLocalCachingProvider.cs
index a2bead29..ebc6275b 100644
--- a/test/EasyCaching.UnitTests/Fake/FakeLocalCachingProvider.cs
+++ b/test/EasyCaching.UnitTests/Fake/FakeLocalCachingProvider.cs
@@ -1,8 +1,8 @@
namespace EasyCaching.UnitTests
{
using System;
- using System.Collections.Generic;
- using System.Threading;
+ using System.Collections.Generic;
+ using System.Threading;
using System.Threading.Tasks;
using EasyCaching.Core;
@@ -94,13 +94,13 @@ public Task>> GetByPrefixAsync(string prefi
public int GetCount(string prefix = "")
{
return 1;
- }
-
- public Task GetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
- {
- return Task.FromResult(1);
- }
-
+ }
+
+ public Task GetCountAsync(string prefix = "", CancellationToken cancellationToken = default)
+ {
+ return Task.FromResult(1);
+ }
+
public TimeSpan GetExpiration(string cacheKey)
{
return TimeSpan.FromSeconds(1);
@@ -151,6 +151,15 @@ public void RemoveByPrefix(string prefix)
}
+ public Task RemoveByPatternAsync(string pattern, CancellationToken cancellationToken = default)
+ {
+ return Task.CompletedTask;
+ }
+
+ public void RemoveByPattern(string pattern)
+ {
+ }
+
public Task RemoveByPrefixAsync(string prefix, CancellationToken cancellationToken = default)
{
return Task.CompletedTask;
diff --git a/test/EasyCaching.UnitTests/SerializerTests/BinaryFormatterSerializerTest.cs b/test/EasyCaching.UnitTests/SerializerTests/BinaryFormatterSerializerTest.cs
deleted file mode 100644
index 829b5878..00000000
--- a/test/EasyCaching.UnitTests/SerializerTests/BinaryFormatterSerializerTest.cs
+++ /dev/null
@@ -1,12 +0,0 @@
-namespace EasyCaching.UnitTests
-{
- using EasyCaching.Core.Serialization;
-
- public class BinaryFormatterSerializerTest : BaseSerializerTest
- {
- public BinaryFormatterSerializerTest()
- {
- _serializer = new DefaultBinaryFormatterSerializer();
- }
- }
-}
\ No newline at end of file