diff --git a/EasyCaching.sln b/EasyCaching.sln
index e8f9f0fc..8dc5aa75 100644
--- a/EasyCaching.sln
+++ b/EasyCaching.sln
@@ -49,6 +49,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.CSRedis", "src\
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Bus.CSRedis", "src\EasyCaching.Bus.CSRedis\EasyCaching.Bus.CSRedis.csproj", "{861E5373-BEF6-4AA2-92C7-8F4941A079E7}"
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Disk", "src\EasyCaching.Disk\EasyCaching.Disk.csproj", "{3D48FD75-01D6-44F9-B7C3-CB6DE784F476}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -139,6 +141,10 @@ Global
{861E5373-BEF6-4AA2-92C7-8F4941A079E7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{861E5373-BEF6-4AA2-92C7-8F4941A079E7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{861E5373-BEF6-4AA2-92C7-8F4941A079E7}.Release|Any CPU.Build.0 = Release|Any CPU
+ {3D48FD75-01D6-44F9-B7C3-CB6DE784F476}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3D48FD75-01D6-44F9-B7C3-CB6DE784F476}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3D48FD75-01D6-44F9-B7C3-CB6DE784F476}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {3D48FD75-01D6-44F9-B7C3-CB6DE784F476}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(NestedProjects) = preSolution
{CE61FAA2-0233-451C-991D-4222ED61C84B} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
@@ -162,5 +168,6 @@ Global
{6EBE36A2-F128-4C63-B90A-B700D8C2F2E8} = {EBB55F65-7D07-4281-8D5E-7B0CA88E1AD0}
{6584761E-E51C-408F-BE51-CA0F6269589B} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
{861E5373-BEF6-4AA2-92C7-8F4941A079E7} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
+ {3D48FD75-01D6-44F9-B7C3-CB6DE784F476} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
EndGlobalSection
EndGlobal
diff --git a/build/releasenotes.props b/build/releasenotes.props
index 2654fab8..774164da 100644
--- a/build/releasenotes.props
+++ b/build/releasenotes.props
@@ -45,5 +45,8 @@
1. Upgrading dependencies.
+
+ 1. init.
+
diff --git a/build/version.props b/build/version.props
index 2fc070ba..6a373beb 100644
--- a/build/version.props
+++ b/build/version.props
@@ -16,5 +16,6 @@
0.5.6
0.5.6
0.5.6
+ 0.5.6
diff --git a/src/EasyCaching.Core/Internal/CachingProviderType.cs b/src/EasyCaching.Core/Internal/CachingProviderType.cs
index d8e198c4..2103f00f 100644
--- a/src/EasyCaching.Core/Internal/CachingProviderType.cs
+++ b/src/EasyCaching.Core/Internal/CachingProviderType.cs
@@ -9,6 +9,7 @@ public enum CachingProviderType
Memcached,
Redis,
SQLite,
+ Disk,
Ext1,
Ext2
}
diff --git a/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs b/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
index e6b95018..89c05689 100644
--- a/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
+++ b/src/EasyCaching.Core/Internal/EasyCachingConstValue.cs
@@ -35,6 +35,11 @@ public class EasyCachingConstValue
///
public const string InMemorySection = "easycaching:inmemory";
+ ///
+ /// The disk section.
+ ///
+ public const string DiskSection = "easycaching:disk";
+
///
/// The redis bus section.
///
@@ -70,6 +75,11 @@ public class EasyCachingConstValue
///
public const string DefaultSQLiteName = "DefaultSQLite";
+ ///
+ /// The default name of the disk.
+ ///
+ public const string DefaultDiskName = "DefaultDisk";
+
///
/// The default name of the serializer.
///
diff --git a/src/EasyCaching.Disk/Configurations/DiskOptions.cs b/src/EasyCaching.Disk/Configurations/DiskOptions.cs
new file mode 100644
index 00000000..d37cc3c9
--- /dev/null
+++ b/src/EasyCaching.Disk/Configurations/DiskOptions.cs
@@ -0,0 +1,13 @@
+namespace EasyCaching.Disk
+{
+ using EasyCaching.Core.Configurations;
+
+ public class DiskOptions : BaseProviderOptions
+ {
+ public DiskOptions()
+ {
+ }
+
+ public DiskDbOptions DBConfig { get; set; } = new DiskDbOptions();
+ }
+}
diff --git a/src/EasyCaching.Disk/Configurations/DiskOptionsExtension.cs b/src/EasyCaching.Disk/Configurations/DiskOptionsExtension.cs
new file mode 100644
index 00000000..7f59f49a
--- /dev/null
+++ b/src/EasyCaching.Disk/Configurations/DiskOptionsExtension.cs
@@ -0,0 +1,49 @@
+namespace EasyCaching.Disk
+{
+ using System;
+ using EasyCaching.Core;
+ using EasyCaching.Core.Configurations;
+ using MessagePack.Resolvers;
+ using Microsoft.AspNetCore.Builder;
+ using Microsoft.Extensions.DependencyInjection;
+ using Microsoft.Extensions.DependencyInjection.Extensions;
+
+ internal sealed class DiskOptionsExtension : IEasyCachingOptionsExtension
+ {
+ ///
+ /// The name.
+ ///
+ private readonly string _name;
+ ///
+ /// The configure.
+ ///
+ private readonly Action configure;
+
+ public DiskOptionsExtension(string name, Action configure)
+ {
+ this._name = name;
+ this.configure = configure;
+ }
+
+ public void AddServices(IServiceCollection services)
+ {
+ services.AddOptions();
+ services.Configure(_name, configure);
+
+ services.TryAddSingleton();
+ services.AddSingleton(x =>
+ {
+ var optionsMon = x.GetRequiredService>();
+ var options = optionsMon.Get(_name);
+ //ILoggerFactory can be null
+ var factory = x.GetService();
+ return new DefaultDiskCachingProvider(_name, options, factory);
+ });
+ }
+
+ public void WithServices(IApplicationBuilder app)
+ {
+ // Method intentionally left empty.
+ }
+ }
+}
diff --git a/src/EasyCaching.Disk/Configurations/EasyCachingOptionsExtensions.cs b/src/EasyCaching.Disk/Configurations/EasyCachingOptionsExtensions.cs
new file mode 100644
index 00000000..62df72dd
--- /dev/null
+++ b/src/EasyCaching.Disk/Configurations/EasyCachingOptionsExtensions.cs
@@ -0,0 +1,40 @@
+namespace EasyCaching.Disk
+{
+ using System;
+ using EasyCaching.Core;
+ using EasyCaching.Core.Configurations;
+ using Microsoft.Extensions.Configuration;
+
+ public static class EasyCachingOptionsExtensions
+ {
+ ///
+ /// Uses the disk caching provider.
+ ///
+ /// Options.
+ /// Configure.
+ /// Name.
+ public static EasyCachingOptions UseDisk(this EasyCachingOptions options, Action configure, string name = EasyCachingConstValue.DefaultDiskName)
+ {
+ ArgumentCheck.NotNull(configure, nameof(configure));
+
+ options.RegisterExtension(new DiskOptionsExtension(name, configure));
+
+ return options;
+ }
+
+ public static EasyCachingOptions UseDisk(this EasyCachingOptions options, IConfiguration configuration, string name = EasyCachingConstValue.DefaultDiskName, string sectionName = EasyCachingConstValue.DiskSection)
+ {
+ var dbConfig = configuration.GetSection(sectionName);
+ var diskOptions = new DiskOptions();
+ dbConfig.Bind(diskOptions);
+
+ void configure(DiskOptions x)
+ {
+ x.EnableLogging = diskOptions.EnableLogging;
+ x.MaxRdSecond = diskOptions.MaxRdSecond;
+ x.DBConfig = diskOptions.DBConfig;
+ }
+ return options.UseDisk(configure, name);
+ }
+ }
+}
diff --git a/src/EasyCaching.Disk/DefaultDiskCachingProvider.Async.cs b/src/EasyCaching.Disk/DefaultDiskCachingProvider.Async.cs
new file mode 100644
index 00000000..8b23d6a3
--- /dev/null
+++ b/src/EasyCaching.Disk/DefaultDiskCachingProvider.Async.cs
@@ -0,0 +1,434 @@
+namespace EasyCaching.Disk
+{
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+ using System.Linq;
+ using System.Threading.Tasks;
+ using EasyCaching.Core;
+ using MessagePack;
+ using Microsoft.Extensions.Logging;
+
+ public partial class DefaultDiskCachingProvider : EasyCachingAbstractProvider
+ {
+ public override async Task BaseExistsAsync(string cacheKey)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey));
+
+ var path = BuildMd5Path(cacheKey);
+
+ if (!File.Exists(path)) return false;
+
+ var val = await GetDiskCacheValueAsync(path);
+
+ return val.Expiration > DateTimeOffset.UtcNow;
+ }
+
+ public override Task BaseFlushAsync()
+ {
+ if (_options.EnableLogging)
+ _logger?.LogInformation("FlushAsync");
+
+ var md5FolderName = GetMd5Str(_name);
+
+ var path = Path.Combine(_options.DBConfig.BasePath, md5FolderName);
+
+ DeleteDirectory(path);
+
+ _cacheKeysMap.Clear();
+
+ return Task.CompletedTask;
+ }
+
+ public override async Task>> BaseGetAllAsync(IEnumerable cacheKeys)
+ {
+ ArgumentCheck.NotNullAndCountGTZero(cacheKeys, nameof(cacheKeys));
+
+ IDictionary> dict = new Dictionary>();
+
+ foreach (var item in cacheKeys)
+ {
+ var path = GetRawPath(item);
+
+ if (!File.Exists(path))
+ {
+ if (!dict.ContainsKey(item))
+ {
+ dict.Add(item, CacheValue.NoValue);
+ }
+ }
+ else
+ {
+ var cached = await GetDiskCacheValueAsync(path);
+
+ if (cached.Expiration > DateTimeOffset.UtcNow)
+ {
+ var t = MessagePackSerializer.Deserialize(cached.Value, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
+
+ if (!dict.ContainsKey(item))
+ {
+ dict.Add(item, new CacheValue(t, true));
+ }
+ }
+ else
+ {
+ if (!dict.ContainsKey(item))
+ {
+ dict.Add(item, CacheValue.NoValue);
+ }
+ }
+ }
+ }
+
+ return dict;
+ }
+
+ public override async Task> BaseGetAsync(string cacheKey, Func> dataRetriever, TimeSpan expiration)
+ {
+ ArgumentCheck.NotNullOrWhiteSpace(cacheKey, nameof(cacheKey));
+ ArgumentCheck.NotNegativeOrZero(expiration, nameof(expiration));
+
+ var path = GetRawPath(cacheKey);
+
+ if (File.Exists(path))
+ {
+ var cached = await GetDiskCacheValueAsync(path);
+
+ if (cached.Expiration > DateTimeOffset.UtcNow)
+ {
+ var t = MessagePackSerializer.Deserialize(cached.Value, MessagePack.Resolvers.ContractlessStandardResolver.Instance);
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"Cache Hit : cachekey = {cacheKey}");
+
+ CacheStats.OnHit();
+
+ return new CacheValue(t, true);
+ }
+ }
+
+ CacheStats.OnMiss();
+
+ if (_options.EnableLogging)
+ _logger?.LogInformation($"Cache Missed : cachekey = {cacheKey}");
+
+ // TODO: how to add mutex key here
+ if (!_cacheKeysMap.TryAdd($"{cacheKey}_Lock", "1"))
+ {
+ System.Threading.Thread.Sleep(_options.SleepMs);
+ return await GetAsync(cacheKey, dataRetriever, expiration);
+ }
+
+ var res = await dataRetriever();
+
+ if (res != null)
+ {
+ await SetAsync(cacheKey, res, expiration);
+ //remove mutex key
+ _cacheKeysMap.TryRemove($"{cacheKey}_Lock", out _);
+ return new CacheValue(res, true);
+ }
+ else
+ {
+ //remove mutex key
+ _cacheKeysMap.TryRemove($"{cacheKey}_Lock", out _);
+ return CacheValue.NoValue;
+ }
+ }
+
+ public override async Task