diff --git a/.gitignore b/.gitignore
index 940794e6..c01b145b 100644
--- a/.gitignore
+++ b/.gitignore
@@ -286,3 +286,7 @@ __pycache__/
*.btm.cs
*.odx.cs
*.xsd.cs
+
+#MACOS
+
+.DS_Store
diff --git a/EasyCaching.sln b/EasyCaching.sln
new file mode 100644
index 00000000..92be08e5
--- /dev/null
+++ b/EasyCaching.sln
@@ -0,0 +1,40 @@
+
+Microsoft Visual Studio Solution File, Format Version 12.00
+# Visual Studio 2012
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{A0F5CC7E-155F-4726-8DEB-E966950B3FE9}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F88D727A-9F9C-43D9-90B1-D4A02BF8BC98}"
+EndProject
+Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{EBB55F65-7D07-4281-8D5E-7B0CA88E1AD0}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Core", "src\EasyCaching.Core\EasyCaching.Core.csproj", "{CE61FAA2-0233-451C-991D-4222ED61C84B}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Memory", "src\EasyCaching.Memory\EasyCaching.Memory.csproj", "{B9490432-737B-4518-B851-9D40FD29B392}"
+EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "EasyCaching.Extensions", "src\EasyCaching.Extensions\EasyCaching.Extensions.csproj", "{679ABDB5-7218-4B4E-A632-10612750CD74}"
+EndProject
+Global
+ GlobalSection(SolutionConfigurationPlatforms) = preSolution
+ Debug|Any CPU = Debug|Any CPU
+ Release|Any CPU = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(ProjectConfigurationPlatforms) = postSolution
+ {CE61FAA2-0233-451C-991D-4222ED61C84B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {CE61FAA2-0233-451C-991D-4222ED61C84B}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {CE61FAA2-0233-451C-991D-4222ED61C84B}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {CE61FAA2-0233-451C-991D-4222ED61C84B}.Release|Any CPU.Build.0 = Release|Any CPU
+ {B9490432-737B-4518-B851-9D40FD29B392}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {B9490432-737B-4518-B851-9D40FD29B392}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {B9490432-737B-4518-B851-9D40FD29B392}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {B9490432-737B-4518-B851-9D40FD29B392}.Release|Any CPU.Build.0 = Release|Any CPU
+ {679ABDB5-7218-4B4E-A632-10612750CD74}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {679ABDB5-7218-4B4E-A632-10612750CD74}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {679ABDB5-7218-4B4E-A632-10612750CD74}.Release|Any CPU.ActiveCfg = Release|Any CPU
+ {679ABDB5-7218-4B4E-A632-10612750CD74}.Release|Any CPU.Build.0 = Release|Any CPU
+ EndGlobalSection
+ GlobalSection(NestedProjects) = preSolution
+ {CE61FAA2-0233-451C-991D-4222ED61C84B} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
+ {B9490432-737B-4518-B851-9D40FD29B392} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
+ {679ABDB5-7218-4B4E-A632-10612750CD74} = {A0F5CC7E-155F-4726-8DEB-E966950B3FE9}
+ EndGlobalSection
+EndGlobal
diff --git a/src/EasyCaching.Core/CacheEntry.cs b/src/EasyCaching.Core/CacheEntry.cs
new file mode 100644
index 00000000..0c41deeb
--- /dev/null
+++ b/src/EasyCaching.Core/CacheEntry.cs
@@ -0,0 +1,53 @@
+namespace EasyCaching.Core
+{
+ using System;
+
+ ///
+ /// Cache entry.
+ ///
+ public class CacheEntry
+ {
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Cache key.
+ /// Cache value.
+ /// Absolute expiration relative to now.
+ public CacheEntry(string cacheKey, object cacheValue, TimeSpan absoluteExpirationRelativeToNow)
+ {
+ if (string.IsNullOrWhiteSpace(cacheKey))
+ throw new ArgumentNullException(nameof(cacheKey));
+
+ if (cacheValue == null)
+ throw new ArgumentNullException(nameof(cacheValue));
+
+ if (absoluteExpirationRelativeToNow <= TimeSpan.Zero)
+ throw new ArgumentOutOfRangeException(
+ nameof(absoluteExpirationRelativeToNow),
+ absoluteExpirationRelativeToNow,
+ "The relative expiration value must be positive.");
+
+ this.CacheKey = cacheKey;
+ this.CacheValue = cacheValue;
+ this.AbsoluteExpirationRelativeToNow = absoluteExpirationRelativeToNow;
+ }
+
+ ///
+ /// Gets the cache key.
+ ///
+ /// The cache key.
+ public string CacheKey { get; private set; }
+
+ ///
+ /// Gets the cache value.
+ ///
+ /// The cache value.
+ public object CacheValue { get; private set; }
+
+ ///
+ /// Gets the absolute expiration relative to now.
+ ///
+ /// The absolute expiration relative to now.
+ public TimeSpan AbsoluteExpirationRelativeToNow { get; private set; }
+ }
+}
diff --git a/src/EasyCaching.Core/EasyCaching.Core.csproj b/src/EasyCaching.Core/EasyCaching.Core.csproj
new file mode 100644
index 00000000..810abdba
--- /dev/null
+++ b/src/EasyCaching.Core/EasyCaching.Core.csproj
@@ -0,0 +1,10 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
diff --git a/src/EasyCaching.Core/EasyCachingManager.cs b/src/EasyCaching.Core/EasyCachingManager.cs
new file mode 100644
index 00000000..1a1cecd5
--- /dev/null
+++ b/src/EasyCaching.Core/EasyCachingManager.cs
@@ -0,0 +1,115 @@
+namespace EasyCaching.Core
+{
+ using System;
+ using System.Collections.Generic;
+
+ ///
+ /// Easycaching manager.
+ ///
+ public class EasyCachingManager
+ {
+ ///
+ /// The caching providers.
+ ///
+ private readonly IEasyCachingProvider[] _cachingProviders;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Caching providers.
+ public EasyCachingManager(IEasyCachingProvider[] cachingProviders)
+ {
+ if (cachingProviders == null || cachingProviders.Length <= 0)
+ {
+ throw new ArgumentNullException(nameof(cachingProviders));
+ }
+
+ this._cachingProviders = cachingProviders;
+ }
+
+ ///
+ /// Get cacheValue by specified cacheKey.
+ ///
+ /// The cacheValue.
+ /// Cache key.
+ public object Get(string cacheKey)
+ {
+ if (string.IsNullOrWhiteSpace(cacheKey))
+ {
+ throw new ArgumentNullException(nameof(cacheKey));
+ }
+
+ object result = null;
+
+ var missed = new HashSet();
+
+ for (int i = 0; i < _cachingProviders.Length; i++)
+ {
+ var cachingProvider = _cachingProviders[i];
+
+ var cacheValue = cachingProvider.Get(cacheKey);
+
+ if (cacheValue != null)
+ {
+ result = cacheValue;
+ break;
+ }
+ else
+ {
+ missed.Add(i);
+ }
+ }
+
+ if (result == null)
+ {
+ result = new EmptyCachingObject();
+ }
+
+ //handle missed cache
+ foreach (var item in missed)
+ {
+ var cachingProvider = _cachingProviders[item];
+
+ var cacheEntry = new CacheEntry(cacheKey,
+ result,
+ result.GetType().Equals(typeof(EmptyCachingObject))
+ ? TimeSpan.FromSeconds(120)
+ : TimeSpan.FromSeconds(3600 + new Random().Next(1, 120)));
+ cachingProvider.Set(cacheEntry);
+ }
+
+ return result;
+ }
+
+ ///
+ /// Set the specified cacheKey, cacheValue and absoluteExpirationRelativeToNow.
+ ///
+ /// The set.
+ /// Cache key.
+ /// Cache value.
+ /// Absolute expiration relative to now.
+ public void Set(string cacheKey, object cacheValue, TimeSpan absoluteExpirationRelativeToNow)
+ {
+ var cacheEntry = new CacheEntry(cacheKey, cacheValue, absoluteExpirationRelativeToNow);
+ this.Set(cacheEntry);
+ }
+
+ ///
+ /// Set the specified cacheEntry.
+ ///
+ /// The set.
+ /// Cache entry.
+ public void Set(CacheEntry cacheEntry)
+ {
+ for (int i = 0; i < _cachingProviders.Length; i++)
+ {
+ var cachingProvider = _cachingProviders[i];
+
+ cacheEntry.AbsoluteExpirationRelativeToNow.Add(TimeSpan.FromSeconds(new Random().Next(1, 120)));
+
+ cachingProvider.Set(cacheEntry);
+ }
+ }
+
+ }
+}
\ No newline at end of file
diff --git a/src/EasyCaching.Core/EmptyCachingObject.cs b/src/EasyCaching.Core/EmptyCachingObject.cs
new file mode 100644
index 00000000..67144e54
--- /dev/null
+++ b/src/EasyCaching.Core/EmptyCachingObject.cs
@@ -0,0 +1,10 @@
+namespace EasyCaching.Core
+{
+ ///
+ /// Empty caching object.
+ ///
+ public class EmptyCachingObject
+ {
+
+ }
+}
diff --git a/src/EasyCaching.Core/IEasyCachingProvider.cs b/src/EasyCaching.Core/IEasyCachingProvider.cs
new file mode 100644
index 00000000..acb00e2c
--- /dev/null
+++ b/src/EasyCaching.Core/IEasyCachingProvider.cs
@@ -0,0 +1,22 @@
+namespace EasyCaching.Core
+{
+ ///
+ /// EasyCaching provider.
+ ///
+ public interface IEasyCachingProvider
+ {
+ ///
+ /// Set the specified cacheEntry.
+ ///
+ ///
+ /// Cache entry.
+ void Set(CacheEntry cacheEntry);
+
+ ///
+ /// Get the specified cacheKey.
+ ///
+ /// The cache value.
+ /// Cache key.
+ object Get(string cacheKey);
+ }
+}
diff --git a/src/EasyCaching.Core/Internal/ICachable.cs b/src/EasyCaching.Core/Internal/ICachable.cs
new file mode 100644
index 00000000..fb71c53e
--- /dev/null
+++ b/src/EasyCaching.Core/Internal/ICachable.cs
@@ -0,0 +1,14 @@
+namespace EasyCaching.Core.Internal
+{
+ ///
+ /// Cachable.
+ ///
+ public interface ICachable
+ {
+ ///
+ /// Gets the cache key.
+ ///
+ /// The cache key.
+ string CacheKey { get; }
+ }
+}
diff --git a/src/EasyCaching.Extensions/CachingInterceptor.cs b/src/EasyCaching.Extensions/CachingInterceptor.cs
new file mode 100644
index 00000000..b59fdcfc
--- /dev/null
+++ b/src/EasyCaching.Extensions/CachingInterceptor.cs
@@ -0,0 +1,173 @@
+namespace EasyCaching.Extensions
+{
+ using System;
+ using System.Collections.Generic;
+ using System.Linq;
+ using System.Reflection;
+ using System.Text;
+ using System.Threading.Tasks;
+ using AspectCore.DynamicProxy;
+ using AspectCore.Injector;
+ using EasyCaching.Core.Internal;
+ using EasyCaching.Core;
+
+ ///
+ /// Caching interceptor.
+ ///
+ public class CachingInterceptor : AbstractInterceptorAttribute
+ {
+
+ ///
+ /// Gets or sets the absolute expiration.
+ ///
+ /// The absolute expiration.
+ public int AbsoluteExpiration { get; set; } = 30;
+
+
+ ///
+ /// Gets or sets the parameter count to generate cache key.
+ ///
+ /// The parameter count.
+ public int ParamCount { get; set; } = 5;
+
+
+ ///
+ /// Gets or sets the cache provider.
+ ///
+ /// The cache provider.
+ [FromContainer]
+ public IEasyCachingProvider CacheProvider { get; set; }
+
+ ///
+ /// The link char of cache key.
+ ///
+ private char _linkChar = ':';
+
+ ///
+ /// Invoke the specified context and next.
+ ///
+ /// The invoke.
+ /// Context.
+ /// Next.
+ public async override Task Invoke(AspectContext context, AspectDelegate next)
+ {
+ var interceptorAttribute = GetInterceptorAttributeInfo(context.ServiceMethod);
+ if (interceptorAttribute != null)
+ {
+ await ProceedCaching(context, next, interceptorAttribute);
+ }
+ else
+ {
+ await next(context);
+ }
+ }
+
+ ///
+ /// Gets the QC aching attribute info.
+ ///
+ /// The QC aching attribute info.
+ /// Method.
+ private CachingInterceptor GetInterceptorAttributeInfo(MethodInfo method)
+ {
+ return method.GetCustomAttributes(true).FirstOrDefault(x => x.GetType() == typeof(CachingInterceptor)) as CachingInterceptor;
+ }
+
+ ///
+ /// Proceeds the caching.
+ ///
+ /// The caching.
+ /// Context.
+ /// Next.
+ /// Attribute.
+ private async Task ProceedCaching(AspectContext context, AspectDelegate next, CachingInterceptor attribute)
+ {
+ var cacheKey = GenerateCacheKey(context, attribute.ParamCount);
+
+ var cacheValue = CacheProvider.Get(cacheKey);
+ if (cacheValue != null)
+ {
+ context.ReturnValue = cacheValue;
+ return;
+ }
+
+ await next(context);
+
+ if (!string.IsNullOrWhiteSpace(cacheKey))
+ {
+ CacheEntry entry = new CacheEntry(cacheKey, context.ReturnValue, TimeSpan.FromSeconds(attribute.AbsoluteExpiration));
+ CacheProvider.Set(entry);
+ }
+ }
+
+ ///
+ /// Generates the cache key.
+ ///
+ /// The cache key.
+ /// Context.
+ /// Parameter count.
+ private string GenerateCacheKey(AspectContext context, int paramCount)
+ {
+ var typeName = context.ServiceMethod.DeclaringType.Name;
+ var methodName = context.ServiceMethod.Name;
+ var methodArguments = this.FormatArgumentsToPartOfCacheKey(context.ServiceMethod.GetParameters(), paramCount);
+
+ return this.GenerateCacheKey(typeName, methodName, methodArguments);
+ }
+
+ ///
+ /// Generates the cache key.
+ ///
+ /// The cache key.
+ /// Type name.
+ /// Method name.
+ /// Parameters.
+ private string GenerateCacheKey(string typeName, string methodName, IList parameters)
+ {
+ var builder = new StringBuilder();
+
+ builder.Append(typeName);
+ builder.Append(_linkChar);
+
+ builder.Append(methodName);
+ builder.Append(_linkChar);
+
+ foreach (var param in parameters)
+ {
+ builder.Append(param);
+ builder.Append(_linkChar);
+ }
+
+ return builder.ToString().TrimEnd(_linkChar);
+ }
+
+ ///
+ /// Formats the arguments to part of cache key.
+ ///
+ /// The arguments to part of cache key.
+ /// Method arguments.
+ /// Max parameter count.
+ private IList FormatArgumentsToPartOfCacheKey(IList methodArguments, int paramCount = 5)
+ {
+ return methodArguments.Select(this.GetArgumentValue).Take(paramCount).ToList();
+ }
+
+ ///
+ /// Gets the argument value.
+ ///
+ /// The argument value.
+ /// Argument.
+ private string GetArgumentValue(object arg)
+ {
+ if (arg is int || arg is long || arg is string)
+ return arg.ToString();
+
+ if (arg is DateTime)
+ return ((DateTime)arg).ToString("yyyyMMddHHmmss");
+
+ if (arg is ICachable)
+ return ((ICachable)arg).CacheKey;
+
+ return null;
+ }
+ }
+}
diff --git a/src/EasyCaching.Extensions/EasyCaching.Extensions.csproj b/src/EasyCaching.Extensions/EasyCaching.Extensions.csproj
new file mode 100644
index 00000000..74044a6a
--- /dev/null
+++ b/src/EasyCaching.Extensions/EasyCaching.Extensions.csproj
@@ -0,0 +1,14 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
+
diff --git a/src/EasyCaching.Memory/EasyCaching.Memory.csproj b/src/EasyCaching.Memory/EasyCaching.Memory.csproj
new file mode 100644
index 00000000..dd07f2d3
--- /dev/null
+++ b/src/EasyCaching.Memory/EasyCaching.Memory.csproj
@@ -0,0 +1,13 @@
+
+
+
+ netstandard2.0
+
+
+
+
+
+
+
+
+
diff --git a/src/EasyCaching.Memory/MemoryCachingProvider.cs b/src/EasyCaching.Memory/MemoryCachingProvider.cs
new file mode 100644
index 00000000..cbc9fd56
--- /dev/null
+++ b/src/EasyCaching.Memory/MemoryCachingProvider.cs
@@ -0,0 +1,49 @@
+namespace EasyCaching.Memory
+{
+ using System;
+ using EasyCaching.Core;
+ using Microsoft.Extensions.Caching.Memory;
+
+ ///
+ /// MemoryCaching provider.
+ ///
+ public class MemoryCachingProvider : IEasyCachingProvider
+ {
+ ///
+ /// The MemoryCache.
+ ///
+ private readonly IMemoryCache _cache;
+
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ /// Microsoft MemoryCache.
+ public MemoryCachingProvider(IMemoryCache cache)
+ {
+ this._cache = cache;
+ }
+
+ ///
+ /// Get cacheValue by specified cacheKey.
+ ///
+ /// The cacheValue.
+ /// Cache key.
+ public object Get(string cacheKey)
+ {
+ if (string.IsNullOrWhiteSpace(cacheKey))
+ throw new ArgumentNullException(nameof(cacheKey));
+
+ return _cache.Get(cacheKey);
+ }
+
+ ///
+ /// Set the specified cacheEntry.
+ ///
+ /// The set.
+ /// Cache entry.
+ public void Set(CacheEntry cacheEntry)
+ {
+ _cache.Set(cacheEntry.CacheKey, cacheEntry.CacheValue, cacheEntry.AbsoluteExpirationRelativeToNow);
+ }
+ }
+}