Skip to content

Commit

Permalink
Feature/cache
Browse files Browse the repository at this point in the history
  • Loading branch information
zhenlei520 authored and blazor-component committed Oct 27, 2021
1 parent af78655 commit 4516689
Show file tree
Hide file tree
Showing 48 changed files with 2,926 additions and 6 deletions.
28 changes: 28 additions & 0 deletions MASA.Utils.sln
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,14 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MASA.Utils.Extensions.Depen
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MASA.Utils.Data.EntityFrameworkCore", "src\Data\MASA.Utils.Data.EntityFrameworkCore\MASA.Utils.Data.EntityFrameworkCore.csproj", "{66CE7FFE-E2DD-43EA-92DB-82A46D1B2215}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MASA.Utils.Caching.Core", "src\Caching\MASA.Utils.Caching.Core\MASA.Utils.Caching.Core.csproj", "{6DBF3335-F4FB-46DE-9FBB-0FA13FB8E822}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MASA.Utils.Caching.DistributedMemory", "src\Caching\MASA.Utils.Caching.DistributedMemory\MASA.Utils.Caching.DistributedMemory.csproj", "{582FB57C-0B6C-4B6C-9766-5E5D8998C0BE}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MASA.Utils.Caching.Memory", "src\Caching\MASA.Utils.Caching.Memory\MASA.Utils.Caching.Memory.csproj", "{6AA5B8CB-E5A7-44E6-8E67-554CB235E6C2}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MASA.Utils.Caching.Redis", "src\Caching\MASA.Utils.Caching.Redis\MASA.Utils.Caching.Redis.csproj", "{60473B6E-3823-4ADE-A960-53B3F88C9C23}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Expand Down Expand Up @@ -77,6 +85,22 @@ Global
{66CE7FFE-E2DD-43EA-92DB-82A46D1B2215}.Debug|Any CPU.Build.0 = Debug|Any CPU
{66CE7FFE-E2DD-43EA-92DB-82A46D1B2215}.Release|Any CPU.ActiveCfg = Release|Any CPU
{66CE7FFE-E2DD-43EA-92DB-82A46D1B2215}.Release|Any CPU.Build.0 = Release|Any CPU
{6DBF3335-F4FB-46DE-9FBB-0FA13FB8E822}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6DBF3335-F4FB-46DE-9FBB-0FA13FB8E822}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6DBF3335-F4FB-46DE-9FBB-0FA13FB8E822}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6DBF3335-F4FB-46DE-9FBB-0FA13FB8E822}.Release|Any CPU.Build.0 = Release|Any CPU
{582FB57C-0B6C-4B6C-9766-5E5D8998C0BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{582FB57C-0B6C-4B6C-9766-5E5D8998C0BE}.Debug|Any CPU.Build.0 = Debug|Any CPU
{582FB57C-0B6C-4B6C-9766-5E5D8998C0BE}.Release|Any CPU.ActiveCfg = Release|Any CPU
{582FB57C-0B6C-4B6C-9766-5E5D8998C0BE}.Release|Any CPU.Build.0 = Release|Any CPU
{6AA5B8CB-E5A7-44E6-8E67-554CB235E6C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6AA5B8CB-E5A7-44E6-8E67-554CB235E6C2}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6AA5B8CB-E5A7-44E6-8E67-554CB235E6C2}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6AA5B8CB-E5A7-44E6-8E67-554CB235E6C2}.Release|Any CPU.Build.0 = Release|Any CPU
{60473B6E-3823-4ADE-A960-53B3F88C9C23}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{60473B6E-3823-4ADE-A960-53B3F88C9C23}.Debug|Any CPU.Build.0 = Debug|Any CPU
{60473B6E-3823-4ADE-A960-53B3F88C9C23}.Release|Any CPU.ActiveCfg = Release|Any CPU
{60473B6E-3823-4ADE-A960-53B3F88C9C23}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
Expand All @@ -95,6 +119,10 @@ Global
{1D6302D8-DA31-4782-A076-AC586A286253} = {4F908878-0EB8-43E4-96E4-8B1F32E9B635}
{00E275C2-3B6A-4F48-AE0B-738AA669226D} = {B2DA607D-4A39-4F0C-A9B3-DD9A061B0B4E}
{66CE7FFE-E2DD-43EA-92DB-82A46D1B2215} = {F844C2A1-C36D-400E-A0D8-7658EF9C3B93}
{6DBF3335-F4FB-46DE-9FBB-0FA13FB8E822} = {A7C058C9-B4A3-472A-A82B-FC4A0A12AC9D}
{582FB57C-0B6C-4B6C-9766-5E5D8998C0BE} = {A7C058C9-B4A3-472A-A82B-FC4A0A12AC9D}
{6AA5B8CB-E5A7-44E6-8E67-554CB235E6C2} = {A7C058C9-B4A3-472A-A82B-FC4A0A12AC9D}
{60473B6E-3823-4ADE-A960-53B3F88C9C23} = {A7C058C9-B4A3-472A-A82B-FC4A0A12AC9D}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {D7DAA0E6-098F-4B18-8775-64FDA96F1FF0}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
namespace MASA.Utils.Caching.Core.DependencyInjection;

public class CachingBuilder : ICachingBuilder
{
/// <summary>
/// Initializes a new instance of the <see cref="CachingBuilder"/> class.
/// </summary>
/// <param name="services">The <see cref="IServiceCollection"/> to add services to.</param>
public CachingBuilder(IServiceCollection services, string name)
{
Services = services;
Name = name;
}

/// <inheritdoc />
public IServiceCollection Services { get; private set; }

/// <inheritdoc />
public string Name { get; private set; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
namespace MASA.Utils.Caching.Core.DependencyInjection;

/// <summary>
/// Extension methods for configuring an <see cref="ICachingBuilder"/>
/// </summary>
public static class DistributedCacheClientBuilderExtensions
{
/// <summary>
/// Adds a delegate that will be used to configure a named <see cref="IDistributedCacheClient"/>.
/// </summary>
/// <param name="builder">The <see cref="ICachingBuilder"/>.</param>
/// <param name="configureOptions">A delegate that is used to configure an <see cref="IDistributedCacheClient"/>.</param>
/// <returns>An <see cref="ICachingBuilder"/> that can be used to configure the client.</returns>
public static ICachingBuilder ConfigureDistributedCacheClient<TOptions>(this ICachingBuilder builder, Action<TOptions> configureOptions) where TOptions : class
{
if (builder == null)
{
throw new ArgumentNullException(nameof(builder));
}

if (configureOptions == null)
{
throw new ArgumentNullException(nameof(configureOptions));
}

builder.Services.Configure(builder.Name, configureOptions);

return builder;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
namespace MASA.Utils.Caching.Core.DependencyInjection;

/// <summary>
/// A builder for configuring named <see cref="ICachingBuilder"/> instances.
/// </summary>
public interface ICachingBuilder
{
/// <summary>
/// Gets the application service collection.
/// </summary>
IServiceCollection Services { get; }

/// <summary>
/// Gets the name of the client configured by this builder.
/// </summary>
string Name { get; }
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
namespace MASA.Utils.Caching.Core;

public abstract class DistributedCacheClientFactory<TOptions> : IDistributedCacheClientFactory
{
private readonly IOptionsMonitor<TOptions> _optionsMonitor;

private readonly ConcurrentDictionary<string, Lazy<IDistributedCacheClient>> _clients;
private readonly Func<string, Lazy<IDistributedCacheClient>> _clientFactory;

public DistributedCacheClientFactory(IOptionsMonitor<TOptions> optionsMonitor)
{
if (optionsMonitor == null)
{
throw new ArgumentNullException(nameof(optionsMonitor));
}

_optionsMonitor = optionsMonitor;

_clients = new ConcurrentDictionary<string, Lazy<IDistributedCacheClient>>();

_clientFactory = (name) =>
{
return new Lazy<IDistributedCacheClient>(() =>
{
return CreateClientHandler(name);
});
};
}

// <inherit />
public IDistributedCacheClient CreateClient(string name)
{
name ??= string.Empty;

var client = _clients.GetOrAdd(name, _clientFactory);

return client.Value;
}

internal protected abstract IDistributedCacheClient CreateClientHandler(string name);

protected TOptions GetOptions(string name)
{
return _optionsMonitor.Get(name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
namespace MASA.Utils.Caching.Core;

/// <summary>
/// Extension methods for <see cref="IDistributedCacheClientFactory"/>.
/// </summary>
public static class DistributedCacheClientFactoryExtensions
{
/// <summary>
/// Creates a new <see cref="IDistributedCacheClient"/> using the default configuration.
/// </summary>
/// <param name="factory">The <see cref="IDistributedCacheClientFactory"/>.</param>
/// <returns>An <see cref="IDistributedCacheClient"/> configured using the default configuration.</returns>
public static IDistributedCacheClient CreateClient(this IDistributedCacheClientFactory factory)
{
if (factory == null)
{
throw new ArgumentNullException(nameof(factory));
}

return factory.CreateClient(string.Empty);
}
}
54 changes: 54 additions & 0 deletions src/Caching/MASA.Utils.Caching.Core/Helpers/SubscribeHelper.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
namespace MASA.Utils.Caching.Core.Helpers;

/// <summary>
/// The subscribe helper.
/// </summary>
public static class SubscribeHelper
{
/// <summary>
/// Formats the memory cache key.
/// </summary>
/// <param name="key">The key.</param>
/// <returns>A string.</returns>
public static string FormatMemoryCacheKey<T>(string key)
{
var type = typeof(T);
if (type.IsGenericType)
{
var dictType = typeof(Dictionary<,>);
if (type.GetGenericTypeDefinition() == dictType)
key += type.Name + "[" + type.GetGenericArguments()[1].Name + "]";
else
key += type.Name + "[" + type.GetGenericArguments()[0].Name + "]";
}
else
{
key += typeof(T).Name;
}

return key;
}

/// <summary>
/// Formats the subscribe channel.
/// </summary>
/// <param name="key">The key.</param>
/// <param name="type">The type.</param>
/// <param name="prefix">The prefix.</param>
/// <returns>A string.</returns>
public static string FormatSubscribeChannel<T>(string key, SubscribeKeyTypes type, string prefix = "")
{
var valueTypeFullName = typeof(T).FullName;
switch (type)
{
case SubscribeKeyTypes.ValueTypeFullName:
return valueTypeFullName;
case SubscribeKeyTypes.ValueTypeFullNameAndKey:
return $"[{valueTypeFullName}]{key}";
case SubscribeKeyTypes.SpecificPrefix:
return $"{prefix}{key}";
default:
throw new NotImplementedException();
}
}
}
97 changes: 97 additions & 0 deletions src/Caching/MASA.Utils.Caching.Core/Interfaces/ICacheClient.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
namespace MASA.Utils.Caching.Core.Interfaces;

/// <summary>
/// The interface for cache client.
/// </summary>
public interface ICacheClient : IDisposable
{
/// <summary>
/// Gets a value with given key.
/// </summary>
/// <param name="key">A string identifying the request value.</param>
/// <returns>The located value or null.</returns>
T Get<T>(string key);

/// <summary>
/// Gets a value with given key.
/// </summary>
/// <param name="key">A string identifying the request value.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous opertion, containing the located value or null.</returns>
Task<T> GetAsync<T>(string key);

/// <summary>
/// Gets or sets a value with given key.
/// </summary>
/// <param name="key">A string identifying the request value.</param>
/// <param name="setter">The setter.</param>
/// <param name="options">The <see cref="CombinedCacheEntryOptions"/>.</param>
/// <returns>The located value.</returns>
T GetOrSet<T>(string key, Func<T> setter, CombinedCacheEntryOptions<T> options = null);

/// <summary>
/// Gets or sets a value with given key.
/// </summary>
/// <param name="key">A string identifying the request value.</param>
/// <param name="setter">The setter.</param>
/// <param name="options">The <see cref="CombinedCacheEntryOptions"/>.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous opertion, containing the located value.</returns>
Task<T> GetOrSetAsync<T>(string key, Func<T> setter, CombinedCacheEntryOptions<T> options = null);

/// <summary>
/// Gets a list of values with given keys.
/// </summary>
/// <param name="keys">A list of string identifying the request value.</param>
/// <returns>The located values without <see langword="null"/>.</returns>
IEnumerable<T> GetList<T>(string[] keys);

/// <summary>
/// Gets a list of values with given keys.
/// </summary>
/// <param name="keys">A list of string identifying the request value.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous opertion, containing the located values with <see langword="null"/>.</returns>
Task<IEnumerable<T>> GetListAsync<T>(string[] keys);

/// <summary>
/// Removes a list of values with given keys.
/// </summary>
/// <param name="keys">A list of string identifying the requested value.</param>
void Remove<T>(params string[] keys);

/// <summary>
/// Removes a list of values with given keys.
/// </summary>
/// <param name="keys">A list of string identifying the requested value.</param>
Task RemoveAsync<T>(params string[] keys);

/// <summary>
/// Sets a value with given key.
/// </summary>
/// <param name="key">A string identifying the requested value.</param>
/// <param name="value">The value to set int the cache.</param>
/// <param name="options">The cache options for the value.</param>
void Set<T>(string key, T value, CombinedCacheEntryOptions<T> options = null);

/// <summary>
/// Sets a value with given key.
/// </summary>
/// <param name="key">A string identifying the requested value.</param>
/// <param name="value">The value to set int the cache.</param>
/// <param name="options">The cache options for the value.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
Task SetAsync<T>(string key, T value, CombinedCacheEntryOptions<T> options = null);

/// <summary>
/// Sets a list of cahce items contains key and value.
/// </summary>
/// <param name="keyValues">The <see cref="Dictionary{TKey, TValue}"/> contains the cache key and cache value.</param>
/// <param name="options">The cache options for the value.</param>
void SetList<T>(Dictionary<string, T> keyValues, CombinedCacheEntryOptions<T> options = null);

/// <summary>
/// Sets a list of cahce items contains key and value.
/// </summary>
/// <param name="keyValues">The <see cref="Dictionary{TKey, TValue}"/> contains the cache key and cache value.</param>
/// <param name="options">The cache options for the value.</param>
/// <returns>The <see cref="Task"/> that represents the asynchronous operation.</returns>
Task SetListAsync<T>(Dictionary<string, T> keyValues, CombinedCacheEntryOptions<T> options = null);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
namespace MASA.Utils.Caching.Core.Interfaces;

/// <summary>
/// A factory abstraction for a component that can create instances of <typeparamref name="TICacheClinet" /> type with custom
/// configuration for a given logical name.
/// </summary>
public interface ICacheClientFactory<TICacheClinet>
{
/// <summary>
/// Creates and configures an instance of <typeparamref name="TICacheClinet" /> type using the configuration that corresponds
/// to the logical name specified by <paramref name="name"/>.
/// </summary>
/// <param name="name">The logical name of the client to create.</param>
/// <returns>A new instance of <typeparamref name="TICacheClinet" /> type.</returns>
TICacheClinet CreateClient(string name);
}
Loading

0 comments on commit 4516689

Please sign in to comment.