using System.Text.Json; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using System.Text.Encodings.Web; using Microsoft.Extensions.Configuration; using System.Xml.Linq; using System.Reflection; namespace DG.Redis { public class RedisManager : IRedisManager { private readonly ILogger _logger; private List<(string, string, int, ConnectionMultiplexer)> _connQueue = new(); public static JsonSerializerOptions jsonSerializerOptions = new JsonSerializerOptions() { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }; public RedisManager( ILogger logger) { _logger = logger; var redisOptions = RedisClient.Default.ConfigurationManager.GetSection("Redis").Get>(); foreach (var option in redisOptions) { var connStr = string.Format("{0}:{1},allowAdmin=true,abortConnect=false,password={2},defaultdatabase={3}", option.HostName, option.Port, option.Password, option.DefaultDatabase ); var options = ConfigurationOptions.Parse(connStr); RedisConnection(option.Name, connStr, option.DefaultDatabase); } } public void RedisConnection(string name, string connStr, int DefaultDatabase) { try { _logger.LogDebug($"Redis config: {connStr}"); _connQueue.Add((name, connStr, DefaultDatabase, ConnectionMultiplexer.Connect(connStr))); _logger.LogInformation($"Redis name: [{name}] manager started!"); } catch (Exception ex) { _logger.LogError($"Redis name: [{name}] connection error: {ex.Message}"); throw; } } public IDatabase GetDatabase(string name = "") { try { var con = _connQueue.FirstOrDefault(); if (!string.IsNullOrEmpty(name)) { con = _connQueue.FirstOrDefault(x => x.Item1 == name); return con.Item4.GetDatabase(con.Item3); } return con.Item4.GetDatabase(con.Item3); } catch { var con = _connQueue.FirstOrDefault(x => x.Item1 == name); var conn = ConnectionMultiplexer.Connect(con.Item2); _logger.LogInformation("Redis manager reconnection!"); return conn.GetDatabase(con.Item3); } } public bool Set(string key, TEntity entity, TimeSpan? cacheTime = null, string name = "") { if (Exists(key, name)) { Remove(key, name); } var result = GetDatabase(name).StringSet(key, JsonSerializer.Serialize(entity, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })); if (cacheTime != null) { return result && Expire(key, cacheTime.Value, name); } return result; } public bool Set(string key, TEntity[] entities, TimeSpan? cacheTime = null, string name = "") { if (Exists(key, name)) { Remove(key, name); } var redisValues = entities.Select(p => (RedisValue)(JsonSerializer.Serialize(p, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }))).ToArray(); var result = GetDatabase(name).SetAdd(key, redisValues) == redisValues.Length; if (cacheTime != null) { return result && Expire(key, cacheTime.Value, name); } return result; } public bool Set(string key, List entities, TimeSpan? cacheTime = null, string name = "") { if (Exists(key, name)) { Remove(key, name); } return Set(key, entities.ToArray(), cacheTime, name); } public async Task SetAsync(string key, TEntity entity, TimeSpan? cacheTime = null, string name = "") { if (await ExistsAsync(key, name)) { await RemoveAsync(key, name); } var result = await GetDatabase(name).StringSetAsync(key, JsonSerializer.Serialize(entity, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })); if (cacheTime != null) { return result && await ExpireAsync(key, cacheTime.Value, name); } return result; } public async Task SetAsync(string key, TEntity[] entities, TimeSpan? cacheTime = null, string name = "") { if (await ExistsAsync(key, name)) { await RemoveAsync(key, name); } var redisValues = entities.Select(p => (RedisValue)(JsonSerializer.Serialize(p, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }))).ToArray(); var result = await GetDatabase(name).SetAddAsync(key, redisValues) == redisValues.Length; if (cacheTime != null) { return result && await ExpireAsync(key, cacheTime.Value, name); } return result; } public async Task SetAsync(string key, List entities, TimeSpan? cacheTime = null, string name = "") { if (await ExistsAsync(key, name)) { await RemoveAsync(key, name); } return await SetAsync(key, entities.ToArray(), cacheTime, name); } public long Count(string key, string name = "") { return GetDatabase(name).ListLength(key); } public async Task CountAsync(string key, string name = "") { return await GetDatabase(name).ListLengthAsync(key); } public bool Exists(string key, string name = "") { return GetDatabase(name).KeyExists(key); } public async Task ExistsAsync(string key, string name = "") { return await GetDatabase(name).KeyExistsAsync(key); } public bool Expire(string key, TimeSpan cacheTime, string name = "") { return GetDatabase(name).KeyExpire(key, DateTime.Now.AddSeconds(int.Parse(cacheTime.TotalSeconds.ToString()))); } public async Task ExpireAsync(string key, TimeSpan cacheTime, string name = "") { return await GetDatabase(name).KeyExpireAsync(key, DateTime.Now.AddSeconds(int.Parse(cacheTime.TotalSeconds.ToString()))); } public bool Remove(string key, string name = "") { return GetDatabase(name).KeyDelete(key); } public bool Remove(string[] keys, string name = "") { var redisKeys = keys.Select(p => (RedisKey)(JsonSerializer.Serialize(p, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }))).ToArray(); return GetDatabase(name).KeyDelete(redisKeys) == redisKeys.Length; } public bool Remove(List keys, string name = "") { return Remove(keys.ToArray(), name); } public async Task RemoveAsync(string key, string name = "") { return await GetDatabase(name).KeyDeleteAsync(key); } public async Task RemoveAsync(string[] keys, string name = "") { var redisKeys = keys.Select(p => (RedisKey)(JsonSerializer.Serialize(p, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping }))).ToArray(); return await GetDatabase(name).KeyDeleteAsync(redisKeys) == redisKeys.Length; } public async Task RemoveAsync(List keys, string name = "") { return await RemoveAsync(keys.ToArray(), name); } public TEntity BlockingDequeue(string key, string name = "") { return JsonSerializer.Deserialize(GetDatabase(name).ListRightPop(key)); } public async Task BlockingDequeueAsync(string key, string name = "") { return JsonSerializer.Deserialize(await GetDatabase(name).ListRightPopAsync(key)); } public TEntity Dequeue(string key, string name = "") { return JsonSerializer.Deserialize(GetDatabase(name).ListLeftPop(key)); } public async Task DequeueAsync(string key, string name = "") { return JsonSerializer.Deserialize(await GetDatabase(name).ListLeftPopAsync(key)); } public void Enqueue(string key, TEntity entity, string name = "") { GetDatabase(name).ListLeftPush(key, JsonSerializer.Serialize(entity, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })); } public async Task EnqueueAsync(string key, TEntity entity, string name = "") { await GetDatabase(name).ListLeftPushAsync(key, JsonSerializer.Serialize(entity, new JsonSerializerOptions { Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping })); } public long Increment(string key, string name = "") { return GetDatabase(name).StringIncrement(key, flags: CommandFlags.FireAndForget); } public async Task IncrementAsync(string key, string name = "") { return await GetDatabase(name).StringIncrementAsync(key, flags: CommandFlags.FireAndForget); } public long Decrement(string key, string value, string name = "") { return GetDatabase(name).HashDecrement(key, value, flags: CommandFlags.FireAndForget); } public async Task DecrementAsync(string key, string value, string name = "") { return await GetDatabase(name).HashDecrementAsync(key, value, flags: CommandFlags.FireAndForget); } public TEntity Get(string key, string name = "") { if (!Exists(key, name)) { return default; } var result = GetDatabase(name).StringGet(key); return JsonSerializer.Deserialize(result); } public List GetList(string key, string name = "") { if (!Exists(key, name)) { return null; } var result = GetDatabase(name).SetMembers(key); return result.Select(p => JsonSerializer.Deserialize(p, jsonSerializerOptions)).ToList(); } public TEntity[] GetArray(string key, string name = "") { if (!Exists(key, name)) { return null; } var result = GetDatabase(name).SetMembers(key); return result.Select(p => JsonSerializer.Deserialize(p, jsonSerializerOptions)).ToArray(); } public async Task GetAsync(string key, string name = "") { if (!await ExistsAsync(key, name)) { return default; } var result = await GetDatabase(name).StringGetAsync(key); return JsonSerializer.Deserialize(result, jsonSerializerOptions); } public async Task GetHashAsync(string key, string name = "") { if (!await ExistsAsync(key, name)) { return default; } var result = await GetDatabase(name).HashGetAllAsync(key); var entity = ConvertFromRedis(result); return entity; } private T ConvertFromRedis(HashEntry[] hashEntries) { PropertyInfo[] properties = typeof(T).GetProperties(); var obj = Activator.CreateInstance(typeof(T)); foreach (var property in properties) { HashEntry entry = hashEntries.FirstOrDefault(g => g.Name.ToString().ToLower().Equals(property.Name.ToLower())); if (entry.Equals(new HashEntry())) continue; property.SetValue(obj, Convert.ChangeType(entry.Value.ToString(), property.PropertyType)); } return (T)obj; } public async Task> GetListAsync(string key, string name = "") { if (!await ExistsAsync(key, name)) { return null; } var result = await GetDatabase(name).SetMembersAsync(key); return result.Select(p => JsonSerializer.Deserialize(p, jsonSerializerOptions)).ToList(); } public async Task GetArrayAsync(string key, string name = "") { if (!await ExistsAsync(key, name)) { return null; } var result = await GetDatabase(name).SetMembersAsync(key); return result.Select(p => JsonSerializer.Deserialize(p, jsonSerializerOptions)).ToArray(); } } }