using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; using Newtonsoft.Json; using NLog; using StackExchange.Redis; using System; using System.Collections.Generic; using System.Linq; using System.Runtime; using System.Text; using System.Threading.Tasks; namespace EgwCoreLib.Lux.Data.Services { /// /// Implementazione interfaccia REDIS: /// - gestione PubSub /// - gestione caching /// public class RedisService : IRedisService { #region Public Constructors public RedisService(IConnectionMultiplexer connection) { _connection = connection; _db = connection.GetDatabase(); _subscriber = connection.GetSubscriber(); // json serializer... FIX errore loop circolare https://www.ryadel.com/en/jsonserializationexception-self-referencing-loop-detected-error-fix-entity-framework-asp-net-core/ JSSettings = new JsonSerializerSettings() { ReferenceLoopHandling = ReferenceLoopHandling.Ignore }; Log.Info("Startup completed"); } #endregion Public Constructors #region Public Methods /// /// Recupero stringa da cache REDIS x key indicata in modalità Async /// /// /// public string? Get(string key) { Log.Trace($"Get request for {key}"); var value = _db.StringGet(key); return value.HasValue ? value.ToString() : null; } /// /// Recupero stringa da cache REDIS x key indicata in modalità Async /// /// /// public async Task GetAsync(string key) { Log.Trace($"GetAsync request for {key}"); var value = await _db.StringGetAsync(key); return value.HasValue ? value.ToString() : null; } /// /// Pubblicazione messaggio su channel /// /// /// /// public long Publish(string channel, string message) { Log.Trace($"Publish: channel {channel}"); RedisChannel rChannel = new RedisChannel(channel, RedisChannel.PatternMode.Literal); long numCli = _subscriber.Publish(rChannel, message); return numCli; } /// /// Pubblicazione messaggio su channel in modalità async /// /// /// /// public async Task PublishAsync(string channel, string message) { Log.Trace($"PublishAsync: channel {channel}"); RedisChannel rChannel = new RedisChannel(channel, RedisChannel.PatternMode.Literal); long numCli = await _subscriber.PublishAsync(rChannel, message); return numCli; } /// /// Conteggio elementi in QUEUE (FIFO) /// /// public long QueueCount(RedisKey queueName) { return _db.ListLength(queueName); } /// /// Conteggio elementi in QUEUE (FIFO) /// /// public async Task QueueCountAsync(RedisKey queueName) { return await _db.ListLengthAsync(queueName); } /// /// Recupero list di TUTTI i valori in QUEUE (FIFO) senza eliminare /// /// /// num max di elementi da recuperare public List QueueListAll(RedisKey queueName) { // lettura + reset in blocco var listData = _db.ListRange(queueName, 0, -1).ToList(); return listData; } /// /// Recupero list di TUTTI i valori in QUEUE (FIFO) senza eliminare /// /// /// num max di elementi da recuperare public async Task> QueueListAllAsync(RedisKey queueName) { // lettura + reset in blocco var listData = await _db.ListRangeAsync(queueName, 0, -1); return listData.ToList(); } /// /// Recupero valore in QUEUE (FIFO) /// /// public RedisValue QueuePop(RedisKey queueName) { return _db.ListLeftPop(queueName); } /// /// Recupero list di TUTTI i valori in QUEUE (FIFO) eliminandoli dalla coda /// /// /// num max di elementi da recuperare public List QueuePopAll(RedisKey queueName) { // lettura + reset in blocco var listData = _db.ListRange(queueName, 0, -1).ToList(); if (listData.Count > 0) { _db.KeyDelete(queueName); // remove the entire list } return listData; } /// /// Recupero list di TUTTI i valori in QUEUE (FIFO) eliminandoli dalla coda in modo Async /// /// /// num max di elementi da recuperare public async Task> QueuePopAllAsync(RedisKey queueName) { // lettura + reset in blocco var rawData = await _db.ListRangeAsync(queueName, 0, -1); var listData = rawData.ToList(); if (listData.Count > 0) { _db.KeyDelete(queueName); // remove the entire list } return listData; } /// /// Recupero valore in QUEUE (FIFO) Async /// /// public async Task QueuePopAsync(RedisKey queueName) { return await _db.ListLeftPopAsync(queueName); } /// /// Recupero una list di valori in QUEUE (FIFO) /// /// /// num max di elementi da recuperare public List QueuePopList(RedisKey queueName, int maxElem) { // nuovo metodo con rimozione var results = new List(maxElem); for (int i = 0; i < maxElem; i++) { var item = _db.ListLeftPop(queueName); if (item.IsNull) break; // queue empty results.Add(item); } return results; } /// /// Recupero una list di valori in QUEUE (FIFO) /// /// /// num max di elementi da recuperare public async Task> QueuePopListAsync(RedisKey queueName, int maxElem) { // nuovo metodo con rimozione var results = new List(maxElem); for (int i = 0; i < maxElem; i++) { var item = await _db.ListLeftPopAsync(queueName); if (item.IsNull) break; // queue empty results.Add(item); } return results; } /// /// Scrittura valore in QUEUE (FIFO) /// /// /// public long QueuePush(RedisKey queueName, RedisValue value) { long qLen = _db.ListRightPush(queueName, value); return qLen; } /// /// Scrittura valore in QUEUE (FIFO) /// /// /// public async Task QueuePushAsync(RedisKey queueName, RedisValue value) { long qLen = await _db.ListRightPushAsync(queueName, value); return qLen; } /// /// Rimuove un valore specifico dalla queue Redis (FIFO) /// /// Nome della lista /// Valore da rimuovere /// Numero di elementi rimossi public long QueueRemove(RedisKey queueName, RedisValue value) { // count = 0 → rimuove TUTTE le occorrenze del valore return _db.ListRemove(queueName, value, 0); } /// /// Rimuove un valore specifico dalla queue Redis (FIFO) /// /// Nome della lista /// Valore da rimuovere /// Numero di elementi rimossi public async Task QueueRemoveAsync(RedisKey queueName, RedisValue value) { // count = 0 → rimuove TUTTE le occorrenze del valore return await _db.ListRemoveAsync(queueName, value, 0); } /// /// Reset della coda /// /// /// public bool QueueReset(RedisKey queueName) { bool answ = _db.KeyDelete(queueName); return answ; } /// /// Reset della coda /// /// /// public async Task QueueResetAsync(RedisKey queueName) { bool answ = await _db.KeyDeleteAsync(queueName); return answ; } /// /// Scrittura string su cache REDIS in modalità Async /// /// /// /// /// public bool Set(string key, string value, TimeSpan? expiry = null) { Log.Trace($"Set request for {key}"); return _db.StringSet(key, value, expiry); } /// /// Scrittura string su cache REDIS in modalità Async /// /// /// /// /// public async Task SetAsync(string key, string value, TimeSpan? expiry = null) { Log.Trace($"SetAsync request for {key}"); return await _db.StringSetAsync(key, value, expiry); } /// /// Sottoscrizione canale REDIS + handler esecuzione /// /// /// public void Subscribe(string channel, Action handler) { Log.Trace($"Subscribed to channel {channel}"); RedisChannel rChannel = new RedisChannel(channel, RedisChannel.PatternMode.Literal); _subscriber.Subscribe(rChannel, handler); } #endregion Public Methods #region Protected Fields /// /// conf speciale serializzatore JSON /// protected JsonSerializerSettings? JSSettings; #endregion Protected Fields #region Private Fields private static Logger Log = LogManager.GetCurrentClassLogger(); private readonly IConnectionMultiplexer _connection; private readonly IDatabase _db; private readonly ISubscriber _subscriber; #endregion Private Fields } }