using Newtonsoft.Json; using NLog; using NLog.Fluent; using StackExchange.Redis; using SteamWare.Logger; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace SteamWare.IO { public class Redis { #region Public Constructors public Redis() { if (Log == null) { Log = LogManager.GetCurrentClassLogger(); } } #endregion Public Constructors #region Public Enums /// /// Tipologia di ordinamento x liste KVP /// public enum kvpOrderBy { /// /// Ordinamento ASCending per KEY /// KeyAsc, /// /// Ordinamento DESCending per KEY /// KeyDesc, /// /// Ordinamento ASCending per VAL /// ValAsc, /// /// Ordinamento DESCending per VAL /// ValDesc } #endregion Public Enums #region Public Properties /// /// Oggetto DB REDIS corrente /// public IDatabase cache { get { IDatabase answ; // se già valorizzato uso oggetto private... if (_currDB != null) { answ = _currDB; } else { // init DB (sullo 0) answ = connRedis.GetDatabase(); // gestione override... if (memLayer.ML.confReadInt("redisDb") >= 0) { // in questo caso uso il DB configurato in app.config... answ = connRedis.GetDatabase(memLayer.ML.confReadInt("redisDb")); } _currDB = answ; } // restituisco oggetto DB return answ; } } /// /// Oggetto statico connessione redis /// public ConnectionMultiplexer connRedis { get { return lazyConnection.Value; } } /// /// Oggetto statico connessione redis /// public ConnectionMultiplexer connRedisAdmin { get { return lazyConnectionAdmin.Value; } } /// /// Restituisce numero record in Redis DB /// public long numRecRedis { get { long answ = 0; try { foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); answ += server.DatabaseSize(); } } catch { } return answ; } } /// /// Oggetto per sottoscrizione Pub/Sub Redis /// public ISubscriber RedPubSub { get { return connRedis.GetSubscriber(); } } #endregion Public Properties #region Public Methods /// /// Effettua comaprazione x VALORE in KVP ASC /// /// /// /// public int CompareVal(KeyValuePair x, KeyValuePair y) { return x.Value.CompareTo(y.Value); } /// /// Effettua comaprazione x VALORE in KVP DESC /// /// /// /// public int CompareValDesc(KeyValuePair x, KeyValuePair y) { return y.Value.CompareTo(x.Value); } /// /// Deserializzazione di un valore string in oggetto generico /// /// /// public object deserializeVal(string serVal) { object answ = ""; try { answ = JsonConvert.DeserializeObject(serVal); } catch { } return answ; } /// /// Restituisce una chiave COUNTER in RedisCache /// /// /// public int getRCnt(string chiave) { int answInt = 0; string answ = ""; try { answ = cache.StringGet(chiave); answInt = Convert.ToInt32(answ); } catch (Exception exc) { Log.Error(string.Format("Eccezione in getRSV:{0}{1}", Environment.NewLine, exc)); } return answInt; } /// /// Restituisce un pò di info sul server redis connesso /// /// public string getRedisInfoData() { string answ = ""; StringBuilder sb = new StringBuilder(); try { sb.AppendLine($"Configuration: {connRedis.Configuration}"); sb.AppendLine($"Connected: {connRedis.IsConnected}"); sb.AppendLine($"ClientName: {connRedis.ClientName}"); sb.AppendLine($"Total Ops: {connRedis.OperationCount}"); sb.AppendLine($"Status: {connRedis.GetStatus()}"); answ = sb.ToString(); } catch { } return answ; } /// /// Restituisce un set KVP (Key Value Pair) salvati in RedisCache /// /// /// public RedisValue[] getRKeys(RedisKey[] chiavi) { RedisValue[] answ = null; try { answ = cache.StringGet(chiavi); } catch (Exception exc) { Log.Error(string.Format("Eccezione in getRKeys:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Restituisce una chiave salvata in RedisCache /// /// /// public string getRSV(string chiave) { string answ = ""; try { answ = cache.StringGet(chiave); } catch (Exception exc) { Log.Error($"Eccezione getRSV{Environment.NewLine}{exc}"); } return answ; } /// /// Lunghezza List /// /// /// public long ListLen(string queueName) { return connRedis.GetDatabase().ListLength((RedisKey)queueName); } /// /// Recupera valore da List (F.I.F.O.) /// /// /// public string ListPop(string queueName) { return connRedis.GetDatabase().ListLeftPop((RedisKey)queueName).ToString(); } /// /// Mette un valore in List (F.I.F.O.) /// /// /// public void ListPush(string queueName, string value) { connRedis.GetDatabase().ListRightPush((RedisKey)queueName, (RedisValue)value); } /// /// Conta num oggetti cache redis che rispondono a pattern /// /// ** = tutti /// public int redCountKey(string keyPattern) { int answ = 0; // cerco se ci sia valore in redis... se vuoto = ALL... keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern; try { foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); foreach (var key in server.Keys(pattern: keyPattern)) { answ++; } } } catch (Exception exc) { Log.Error($"Eccezione in redCountKey:{Environment.NewLine}{exc}"); } return answ; } /// /// ELIMINA un SINGOLO VALORE dalla hash (dato field = chiave) /// /// /// /// public bool redDelHashField(string hashKey, string hashField) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; RedisValue campo = hashField; answ = cache.HashDelete(chiave, campo); } catch (Exception exc) { Log.Error($"Errore in redDelHashField{Environment.NewLine}{exc}"); } return answ; } /// /// Elimina una key (hash, string) /// /// /// public bool redDelKey(string key) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = key; cache.KeyDelete(chiave); answ = true; } catch { } return answ; } /// /// Flush completo cache redis /// /// ** = tutti /// public bool redFlushKey(string keyPattern) { bool answ = false; // cerco se ci sia valore in redis... se vuoto = ALL... keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern; try { foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); var keys = server.Keys(database: memLayer.ML.confReadInt("redisDb"), pattern: $"{keyPattern}*"); foreach (var key in keys) { cache.KeyDelete(key); } } answ = true; } catch (Exception exc) { Log.Error($"Eccezione in redFlushKey{Environment.NewLine}{exc}"); } return answ; } /// /// Restituisce oggetti cache redis che rispondono a pattern /// /// ** = tutti /// Tipo di ordinamento per kvp /// public List> redGetCounterByKey(string keyPattern, kvpOrderBy orderBy) { int numAnsw = redCountKey(keyPattern); RedisKey[] chiavi = new RedisKey[numAnsw]; List> answ = new List>(); // se vuoto = ALL... keyPattern = string.IsNullOrEmpty(keyPattern) ? "**" : keyPattern; // recupero in primis elenco chiavi try { int i = 0; foreach (var ep in connRedis.GetEndPoints()) { var server = connRedis.GetServer(ep); foreach (var key in server.Keys(pattern: keyPattern)) { chiavi[i] = key; i++; } } } catch (Exception exc) { Log.Error($"Eccezione in redGetCounterByKey{Environment.NewLine}{exc}"); } // ora recupero valori! var valori = getRKeys(chiavi); int currVal = 0; // popolo rispsota try { for (int i = 0; i < numAnsw; i++) { Int32.TryParse(valori[i], out currVal); answ.Add(new KeyValuePair(chiavi[i], currVal)); } } catch { } // se richiesto riordino... switch (orderBy) { case kvpOrderBy.KeyAsc: answ.Sort(CompareKey); break; case kvpOrderBy.KeyDesc: answ.Sort(CompareKeyDesc); break; case kvpOrderBy.ValAsc: answ.Sort(CompareVal); break; case kvpOrderBy.ValDesc: answ.Sort(CompareValDesc); break; default: break; } return answ; } /// /// Recupera tutti i valori dalla hash /// /// /// public KeyValuePair[] redGetHash(string hashKey) { KeyValuePair[] answ = new KeyValuePair[1]; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = cache.HashGetAll(chiave); answ = new KeyValuePair[valori.Length]; int i = 0; foreach (HashEntry item in valori) { answ[i] = new KeyValuePair(item.Name, item.Value); i++; } } catch { } return answ; } /// /// Recupera tutti i valori dalla hash in formato Dictionary /// /// /// public Dictionary redGetHashDict(string hashKey) { Dictionary answ = new Dictionary(); // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = cache.HashGetAll(chiave); foreach (HashEntry item in valori) { answ.Add(item.Name, item.Value); } } catch { } return answ; } /// /// Recupera UN SINGOLO VALORE dalla hash per un dato field /// /// /// /// public string redGetHashField(string hashKey, string hashField) { string answ = ""; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; RedisValue campo = hashField; RedisValue valOut = cache.HashGet(chiave, campo); answ = valOut.ToString(); } catch { } return answ; } /// /// Nome della variabile HASH da utilizzare (dato CodModulo / Server / DB impiegato da /// funzionalita' DbConfig) + keyName richiesto... /// public string redHash(string keyName) { string answ = keyName; try { answ = string.Format("{0}:{1}:{2}:{3}", memLayer.ML.confReadString("CodModulo"), memLayer.ML.taConfig.Connection.DataSource, memLayer.ML.taConfig.Connection.Database, keyName).Replace("\\", "_"); } catch { } return answ; } /// /// Verifica se ci siano valori nella hash indicata... /// /// /// public bool redHashPresent(RedisKey key) { bool answ = false; // cerco se ci sia valore in redis... try { answ = cache.HashGetAll(key).Length > 0; } catch (Exception exc) { Log.Error(string.Format("Eccezione in redHashPresent per la key {2}{0}{1}", Environment.NewLine, exc, key)); } return answ; } /// /// Verifica se ci siano valori nella hash indicata (string) /// /// /// public bool redHashPresentSz(string key) { bool answ = false; try { RedisKey chiave = key; answ = redHashPresent(chiave); } catch { } return answ; } /// /// Verifica se ci siano valori nella KEY indicata... /// /// /// public bool redKeyPresent(RedisKey key) { bool answ = false; // cerco se ci sia valore in redis... try { answ = cache.KeyExists(key); } catch (Exception exc) { Log.Error(string.Format("Eccezione in redKeyPresent per la key {2}:{0}{1}", Environment.NewLine, exc, key)); } return answ; } /// /// Verifica se ci siano valori nella KEY indicata (string) /// /// /// public bool redKeyPresentSz(string key) { bool answ = false; try { RedisKey chiave = key; answ = redKeyPresent(chiave); } catch { } return answ; } /// /// Salvataggio di una hash di valori /// /// chiave /// valori /// public bool redSaveHash(string hashKey, KeyValuePair[] hashFields) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = new HashEntry[hashFields.Length]; int i = 0; foreach (KeyValuePair kvp in hashFields) { valori[i] = new HashEntry(kvp.Key, kvp.Value); i++; } cache.HashSet(chiave, valori); answ = true; } catch { } return answ; } /// /// Salvataggio di una hash di valori /// /// chiave /// valori /// /// scadenza preimpostata hash (secondi) | defaoult = -1 (non scade) /// /// public bool redSaveHash(string hashKey, KeyValuePair[] hashFields, double expireSeconds = -1) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; answ = redSaveHash(hashKey, hashFields); if (expireSeconds > 0) { cache.KeyExpire(chiave, DateTime.Now.AddSeconds(expireSeconds)); } //answ = true; } catch { } return answ; } /// /// Salvataggio di una hash di valori in formato Dictionary /// /// chiave /// valori /// public bool redSaveHashDict(string hashKey, Dictionary hashFields) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; HashEntry[] valori = new HashEntry[hashFields.Count]; int i = 0; foreach (KeyValuePair kvp in hashFields) { valori[i] = new HashEntry(kvp.Key, kvp.Value); i++; } cache.HashSet(chiave, valori); answ = true; } catch { } return answ; } /// /// Salvataggio di una hash di valori in formato Dictionary /// /// chiave /// valori /// /// scadenza preimpostata hash (secondi) | defaoult = -1 (non scade) /// /// public bool redSaveHashDict(string hashKey, Dictionary hashFields, double expireSeconds = -1) { bool answ = false; // cerco se ci sia valore in redis... try { RedisKey chiave = hashKey; answ = redSaveHashDict(hashKey, hashFields); if (expireSeconds > 0) { cache.KeyExpire(chiave, DateTime.Now.AddSeconds(expireSeconds)); } //answ = true; } catch { } return answ; } /// /// Salvataggio di una hash di valori /// /// chiave /// valori come lista KVP /// public bool redSaveHashList(string hashKey, List> hashListKVP) { bool answ = false; if (connRedis.IsConnected) { // cerco se ci sia valore in redis... IDatabase cache = connRedis.GetDatabase(); try { RedisKey chiave = hashKey; HashEntry[] valori = new HashEntry[hashListKVP.Count]; int i = 0; foreach (KeyValuePair kvp in hashListKVP) { valori[i] = new HashEntry(kvp.Key, kvp.Value); i++; } cache.HashSet(chiave, valori); answ = true; } catch { } } return answ; } /// /// Restituisce info dei server connessi... /// /// public IServer[] redServInfo() { IServer[] answ = new IServer[1]; try { answ = new IServer[connRedisAdmin.GetEndPoints().Length]; int i = 0; foreach (var ep in connRedisAdmin.GetEndPoints()) { var server = connRedisAdmin.GetServer(ep); answ[i] = server; i++; } } catch (Exception exc) { Log.Error($"redServInfo:{Environment.NewLine}{exc}"); } return answ; } /// /// Resetta (elimina) un contatore in Redis /// /// /// public bool resetRCnt(string chiave) { bool answ = false; try { answ = cache.KeyDelete(chiave); } catch (Exception exc) { Log.Error(string.Format("Eccezione in resetRCnt:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Serializzazione di un oggetto generico /// /// /// public string serializeVal(object origVal) { string answ = ""; try { answ = JsonConvert.SerializeObject(origVal); } catch { } return answ; } /// /// Decrementa un contatore in Redis /// /// /// public long setRCntD(string chiave) { long answ = 0; try { answ = cache.StringDecrement(chiave, 1); } catch (Exception exc) { Log.Error(string.Format("Eccezzione in setRCD:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Incrementa un contatore in Redis /// /// /// public long setRCntI(string chiave) { long answ = 0; try { answ = cache.StringIncrement(chiave, 1); } catch (Exception exc) { Log.Error(string.Format("Eccezzione in setRCI:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Salva un set KVP (Key Value Pair) in RedisCache /// /// Set KVP chiave-valore da salvare /// public bool setRKeys(KeyValuePair[] valori) { bool answ = false; try { cache.StringSet(valori); answ = true; } catch (Exception exc) { Log.Error(string.Format("Eccezione in setRKeys:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Salva una chiave in RedisCache /// /// /// /// public bool setRSV(string chiave, string valore) { bool answ = false; try { cache.StringSet(chiave, valore); answ = true; } catch (Exception exc) { Log.Error(string.Format("Eccezzione in setRSV:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Salva una chiave in RedisCache /// /// /// /// in secondi /// public bool setRSV(string chiave, string valore, int TTL_sec) { bool answ = false; try { TimeSpan expT = new TimeSpan(0, 0, TTL_sec); // salvo con expyry... cache.StringSet(chiave, valore, expT); answ = true; } catch (Exception exc) { Log.Error(string.Format("Eccezzione in setRSV:{0}{1}", Environment.NewLine, exc)); } return answ; } /// /// Lunghezza Stack /// /// /// public long StackLen(string stackName) { return connRedis.GetDatabase().ListLength((RedisKey)stackName); } /// /// Recupera valore da Stack (F.I.L.O.) /// /// /// public string StackPop(string stackName) { return connRedis.GetDatabase().ListRightPop((RedisKey)stackName).ToString(); } /// /// Mette in Stack un valore (F.I.L.O.) /// /// /// public void StackPush(string stackName, string value) { connRedis.GetDatabase().ListRightPush((RedisKey)stackName, (RedisValue)value); } #endregion Public Methods #region Private Fields private static NLog.Logger Log = LogManager.GetCurrentClassLogger(); /// /// Connessione lazy a redis... /// private Lazy lazyConnection = new Lazy(() => { string RedisConn = memLayer.ML.confReadString("RedisConn"); if (string.IsNullOrEmpty(RedisConn)) { RedisConn = "localhost,abortConnect=false,ssl=false"; } return ConnectionMultiplexer.Connect(RedisConn); }); /// /// Connessione lazy a redis... /// private Lazy lazyConnectionAdmin = new Lazy(() => { string RedisConnAdmin = memLayer.ML.confReadString("RedisConnAdmin"); if (string.IsNullOrEmpty(RedisConnAdmin)) { RedisConnAdmin = "localhost,abortConnect=false,ssl=false,allowAdmin=true"; } return ConnectionMultiplexer.Connect(RedisConnAdmin); }); #endregion Private Fields #region Private Properties /// /// Oggetto currentDb locale /// private IDatabase _currDB { get; set; } #endregion Private Properties #region Private Methods /// /// Effettua comaprazione x CHIAVE in KVP ASC /// /// /// /// private int CompareKey(KeyValuePair x, KeyValuePair y) { return x.Key.CompareTo(y.Key); } /// /// Effettua comaprazione x CHIAVE in KVP DESC /// /// /// /// private int CompareKeyDesc(KeyValuePair x, KeyValuePair y) { return y.Key.CompareTo(x.Key); } #endregion Private Methods } }