代码上传
This commit is contained in:
@@ -0,0 +1,31 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YLErp.Cache
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存配置
|
||||
/// </summary>
|
||||
public class CacheConfig
|
||||
{
|
||||
/// <summary>
|
||||
/// 是否可用
|
||||
/// </summary>
|
||||
public bool Enable { get; set; } = false;
|
||||
/// <summary>
|
||||
/// 连接字符串
|
||||
/// </summary>
|
||||
public string ConnectionString { get; set; }
|
||||
/// <summary>
|
||||
/// 数据库号
|
||||
/// </summary>
|
||||
public int DataBaseNum { get; set; } = 0;
|
||||
/// <summary>
|
||||
/// 缓存key的自定义前缀(Option)
|
||||
/// </summary>
|
||||
public string CustomPrefix { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace YLErp.Cache
|
||||
{
|
||||
public class CacheModule
|
||||
{
|
||||
public static void InitModule(IServiceCollection services)
|
||||
{
|
||||
services.AddYLCache(option => { });
|
||||
}
|
||||
}
|
||||
|
||||
public static class CacheServiceExtensions
|
||||
{
|
||||
public static void AddYLCache(this IServiceCollection services, Action<CacheConfig> optionAction)
|
||||
{
|
||||
services.AddTransient<IYLCache, YLRedisCache>();
|
||||
services.Configure(optionAction);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
using Newtonsoft.Json;
|
||||
using StackExchange.Redis;
|
||||
|
||||
namespace YLErp.Cache
|
||||
{
|
||||
public interface IYLCache
|
||||
{
|
||||
/// <summary>
|
||||
/// 缓存是否可用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
bool CacheEnable();
|
||||
/// <summary>
|
||||
/// 字符串数据类型的保存
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
bool StringSet(string key,string value);
|
||||
|
||||
/// <summary>
|
||||
/// 字符串数据类型的查询
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <returns></returns>
|
||||
string StringGet(string key);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 字符串数据类型的保存
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
bool StringSet<T>(string key, object value);
|
||||
/// <summary>
|
||||
/// 字符串数据类型的查询
|
||||
/// </summary>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
T StringGet<T>(string key) where T : class;
|
||||
|
||||
bool StringSetWithNoPrefix<T>(string key, object value) where T : class;
|
||||
T StringGetWithNoPrefix<T>(string key) where T : class;
|
||||
#region Batch Operate
|
||||
/// <summary>
|
||||
/// 批量增加
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="preKey"></param>
|
||||
/// <param name="setSize"></param>
|
||||
/// <param name="values"></param>
|
||||
Task BatchAdd<T>(string preKey, List<T> values, int listSize = 1000);
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="keys"></param>
|
||||
bool BatchDelete(string pattern);
|
||||
|
||||
/// <summary>
|
||||
/// 批量查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
List<T> BatchQuery<T>(string pattern);
|
||||
|
||||
/// <summary>
|
||||
/// 模糊查询
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
IEnumerable<RedisKey> QueryKeys(string pattern);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net6.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<NoWarn>NU1803</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="6.0.0" />
|
||||
<PackageReference Include="Microsoft.Extensions.Options" Version="6.0.0" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
|
||||
<PackageReference Include="StackExchange.Redis" Version="2.6.90" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -0,0 +1,239 @@
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Options;
|
||||
using Newtonsoft.Json;
|
||||
using Newtonsoft.Json.Linq;
|
||||
using StackExchange.Redis;
|
||||
using StackExchange.Redis.KeyspaceIsolation;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace YLErp.Cache
|
||||
{
|
||||
public class YLRedisCache : IYLCache
|
||||
{
|
||||
private readonly IOptions<CacheConfig> _cacheConfig;
|
||||
private readonly ConnectionMultiplexer _redisConnector;
|
||||
private readonly IServer redisServer;
|
||||
private IDatabase db;
|
||||
|
||||
public YLRedisCache(IOptions<CacheConfig> cacheConfig)
|
||||
{
|
||||
_cacheConfig = cacheConfig;
|
||||
if (_cacheConfig.Value.Enable)
|
||||
{
|
||||
var redisOptions= ConfigurationOptions.Parse(cacheConfig.Value.ConnectionString);
|
||||
_redisConnector = ConnectionMultiplexer.Connect(redisOptions);
|
||||
redisServer = _redisConnector.GetServer(redisOptions.EndPoints.FirstOrDefault());
|
||||
db = _redisConnector.GetDatabase(cacheConfig.Value.DataBaseNum);
|
||||
//db.WithKeyPrefix(GenerateKey(""));
|
||||
}
|
||||
}
|
||||
/// <summary>
|
||||
/// 缓存是否可用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public bool CacheEnable()
|
||||
{
|
||||
return _cacheConfig.Value.Enable;
|
||||
}
|
||||
|
||||
public bool StringSet(string key, string value)
|
||||
{
|
||||
return db.StringSet(GenerateKey(key), value);
|
||||
}
|
||||
|
||||
public string StringGet(string key)
|
||||
{
|
||||
return db.StringGet(GenerateKey(key));
|
||||
}
|
||||
|
||||
public bool StringSet<T>(string key, object value)
|
||||
{
|
||||
return StringSet(key, JsonConvert.SerializeObject(value));
|
||||
}
|
||||
|
||||
public T StringGet<T>(string key) where T : class
|
||||
{
|
||||
var value = StringGet(key);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
|
||||
public T StringGetWithNoPrefix<T>(string key) where T : class
|
||||
{
|
||||
var value = db.StringGet(key);
|
||||
if (string.IsNullOrEmpty(value))
|
||||
{
|
||||
return null;
|
||||
}
|
||||
return JsonConvert.DeserializeObject<T>(value);
|
||||
}
|
||||
|
||||
#region Batch Operate
|
||||
private async Task<List<RedisValue>> SerializeObject<T>(List<T> objs)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
List<RedisValue> redisValues = new List<RedisValue>();
|
||||
foreach (T obj in objs)
|
||||
{
|
||||
redisValues.Add(JsonConvert.SerializeObject(obj));
|
||||
}
|
||||
return redisValues;
|
||||
});
|
||||
}
|
||||
|
||||
private async Task<RedisValue> SerializeObject<T>(T obj)
|
||||
{
|
||||
return await Task.Factory.StartNew(() =>
|
||||
{
|
||||
return JsonConvert.SerializeObject(obj);
|
||||
});
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量增加
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="preKey"></param>
|
||||
/// <param name="setSize"></param>
|
||||
/// <param name="values"></param>
|
||||
public async Task BatchAdd<T>(string preKey,List<T> values, int setSize = 1000)
|
||||
{
|
||||
//Stopwatch stopwatch1= Stopwatch.StartNew();
|
||||
//List<Task> tasktest=new List<Task>();
|
||||
//foreach (var obj in values)
|
||||
//{
|
||||
// tasktest.Add(SerializeObject(obj));
|
||||
//}
|
||||
//await Task.WhenAll(tasktest);
|
||||
//stopwatch1.Stop();
|
||||
//Console.WriteLine($"测试序列化耗时:{stopwatch1.ElapsedMilliseconds}");
|
||||
|
||||
//for (var i = 0; i < values.Count; i++)
|
||||
//{
|
||||
// int page = i / setSize;
|
||||
// if (!dicRedisValue.ContainsKey(page))
|
||||
// {
|
||||
// dicRedisValue.Add(page, new List<RedisValue>());
|
||||
// }
|
||||
// dicRedisValue[page].Add(JsonConvert.SerializeObject(values[i]));
|
||||
//}
|
||||
|
||||
|
||||
//var batch = db.CreateBatch();
|
||||
//List<Task> tasks = new List<Task>();
|
||||
//for (int i = 0; i < values.Count; i++)
|
||||
//{
|
||||
// int page = i / setSize;
|
||||
// var key = GenerateKey($"{preKey}:{page}");
|
||||
// tasks.Add(batch.SetAddAsync(key, JsonConvert.SerializeObject(values[i])));
|
||||
//}
|
||||
|
||||
Stopwatch stopwatch = Stopwatch.StartNew();
|
||||
// 数据分组
|
||||
Dictionary<int, Task<List<RedisValue>>> dicRedisValue = new Dictionary<int, Task<List<RedisValue>>>();
|
||||
var valueCount = values.Count;
|
||||
var pageCount = valueCount % setSize > 0 ? (valueCount / setSize) + 1 : (valueCount / setSize);
|
||||
var page = 0;
|
||||
while (page <=pageCount)
|
||||
{
|
||||
var takeDataCount = page * setSize > valueCount ? valueCount - (page - 1) * setSize : setSize;
|
||||
var datas = values.Skip(page * setSize).Take(takeDataCount);
|
||||
dicRedisValue.Add(page, SerializeObject(datas.ToList()));
|
||||
page++;
|
||||
}
|
||||
await Task.WhenAll(dicRedisValue.Values);
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine($"Redis批量保存序列化耗时:{stopwatch.ElapsedMilliseconds}");
|
||||
|
||||
// 保存数据
|
||||
stopwatch.Restart();
|
||||
List<Task> tasks = new List<Task>();
|
||||
// 每组对应一个Set
|
||||
var batch = db.CreateBatch();
|
||||
foreach (int itemKey in dicRedisValue.Keys)
|
||||
{
|
||||
var key = GenerateKey($"{preKey}:{itemKey}");
|
||||
var value = dicRedisValue[itemKey].Result.ToArray();
|
||||
tasks.Add(batch.SetAddAsync(key, value));
|
||||
}
|
||||
|
||||
batch.Execute();
|
||||
await Task.WhenAll(tasks);
|
||||
stopwatch.Stop();
|
||||
Console.WriteLine($"redis 添加执行耗时:{stopwatch.ElapsedMilliseconds}");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量删除
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="keys"></param>
|
||||
public bool BatchDelete(string pattern)
|
||||
{
|
||||
var keys = QueryKeys(pattern);
|
||||
return db.KeyDelete(keys.ToArray())>0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 批量查询
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="pattern"></param>
|
||||
/// <returns></returns>
|
||||
public List<T> BatchQuery<T>(string pattern)
|
||||
{
|
||||
var keys = QueryKeys(pattern);
|
||||
if (!keys.Any())
|
||||
{
|
||||
return new List<T>();
|
||||
}
|
||||
var redisValues = db.SetCombine(SetOperation.Union, keys.ToArray());
|
||||
return redisValues
|
||||
?.Select(g => JsonConvert.DeserializeObject<T>(g.ToString()))
|
||||
?.ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 模糊查询
|
||||
/// </summary>
|
||||
/// <param name="pattern"></param>
|
||||
public IEnumerable<RedisKey> QueryKeys(string pattern)
|
||||
{
|
||||
return redisServer.Keys(db.Database, GenerateKey(pattern));
|
||||
}
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 生成带固定前缀Otc的Key
|
||||
/// </summary>
|
||||
/// <param name="originKey"></param>
|
||||
/// <returns></returns>
|
||||
public string GenerateKey(string originKey)
|
||||
{
|
||||
return $"Otc:{originKey}";
|
||||
}
|
||||
/// <summary>
|
||||
/// 保存不带前缀的key
|
||||
/// </summary>
|
||||
/// <typeparam name="T"></typeparam>
|
||||
/// <param name="key"></param>
|
||||
/// <param name="value"></param>
|
||||
/// <returns></returns>
|
||||
public bool StringSetWithNoPrefix<T>(string key, object value) where T : class
|
||||
{
|
||||
return db.StringSet(key, JsonConvert.SerializeObject(value));
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user