Files
lux/EgwCoreLib.Lux.Data/Services/RedisService.cs
T
2025-12-09 16:39:04 +01:00

349 lines
12 KiB
C#

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
{
/// <summary>
/// Implementazione interfaccia REDIS:
/// - gestione PubSub
/// - gestione caching
/// </summary>
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
/// <summary>
/// Recupero stringa da cache REDIS x key indicata in modalità Async
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public string? Get(string key)
{
Log.Trace($"Get request for {key}");
var value = _db.StringGet(key);
return value.HasValue ? value.ToString() : null;
}
/// <summary>
/// Recupero stringa da cache REDIS x key indicata in modalità Async
/// </summary>
/// <param name="key"></param>
/// <returns></returns>
public async Task<string?> GetAsync(string key)
{
Log.Trace($"GetAsync request for {key}");
var value = await _db.StringGetAsync(key);
return value.HasValue ? value.ToString() : null;
}
/// <summary>
/// Pubblicazione messaggio su channel
/// </summary>
/// <param name="channel"></param>
/// <param name="message"></param>
/// <returns></returns>
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;
}
/// <summary>
/// Pubblicazione messaggio su channel in modalità async
/// </summary>
/// <param name="channel"></param>
/// <param name="message"></param>
/// <returns></returns>
public async Task<long> 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;
}
/// <summary>
/// Conteggio elementi in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
public long QueueCount(RedisKey queueName)
{
return _db.ListLength(queueName);
}
/// <summary>
/// Conteggio elementi in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
public async Task<long> QueueCountAsync(RedisKey queueName)
{
return await _db.ListLengthAsync(queueName);
}
/// <summary>
/// Recupero list di TUTTI i valori in QUEUE (FIFO) senza eliminare
/// </summary>
/// <param name="queueName"></param>
/// <param name="maxElem">num max di elementi da recuperare</param>
public List<RedisValue> QueueListAll(RedisKey queueName)
{
// lettura + reset in blocco
var listData = _db.ListRange(queueName, 0, -1).ToList();
return listData;
}
/// <summary>
/// Recupero list di TUTTI i valori in QUEUE (FIFO) senza eliminare
/// </summary>
/// <param name="queueName"></param>
/// <param name="maxElem">num max di elementi da recuperare</param>
public async Task<List<RedisValue>> QueueListAllAsync(RedisKey queueName)
{
// lettura + reset in blocco
var listData = await _db.ListRangeAsync(queueName, 0, -1);
return listData.ToList();
}
/// <summary>
/// Recupero valore in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
public RedisValue QueuePop(RedisKey queueName)
{
return _db.ListLeftPop(queueName);
}
/// <summary>
/// Recupero list di TUTTI i valori in QUEUE (FIFO) eliminandoli dalla coda
/// </summary>
/// <param name="queueName"></param>
/// <param name="maxElem">num max di elementi da recuperare</param>
public List<RedisValue> 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;
}
/// <summary>
/// Recupero list di TUTTI i valori in QUEUE (FIFO) eliminandoli dalla coda in modo Async
/// </summary>
/// <param name="queueName"></param>
/// <param name="maxElem">num max di elementi da recuperare</param>
public async Task<List<RedisValue>> 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;
}
/// <summary>
/// Recupero valore in QUEUE (FIFO) Async
/// </summary>
/// <param name="queueName"></param>
public async Task<RedisValue> QueuePopAsync(RedisKey queueName)
{
return await _db.ListLeftPopAsync(queueName);
}
/// <summary>
/// Recupero una list di valori in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
/// <param name="maxElem">num max di elementi da recuperare</param>
public List<RedisValue> QueuePopList(RedisKey queueName, int maxElem)
{
// nuovo metodo con rimozione
var results = new List<RedisValue>(maxElem);
for (int i = 0; i < maxElem; i++)
{
var item = _db.ListLeftPop(queueName);
if (item.IsNull) break; // queue empty
results.Add(item);
}
return results;
}
/// <summary>
/// Recupero una list di valori in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
/// <param name="maxElem">num max di elementi da recuperare</param>
public async Task<List<RedisValue>> QueuePopListAsync(RedisKey queueName, int maxElem)
{
// nuovo metodo con rimozione
var results = new List<RedisValue>(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;
}
/// <summary>
/// Scrittura valore in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
/// <param name="value"></param>
public long QueuePush(RedisKey queueName, RedisValue value)
{
long qLen = _db.ListRightPush(queueName, value);
return qLen;
}
/// <summary>
/// Scrittura valore in QUEUE (FIFO)
/// </summary>
/// <param name="queueName"></param>
/// <param name="value"></param>
public async Task<long> QueuePushAsync(RedisKey queueName, RedisValue value)
{
long qLen = await _db.ListRightPushAsync(queueName, value);
return qLen;
}
/// <summary>
/// Rimuove un valore specifico dalla queue Redis (FIFO)
/// </summary>
/// <param name="queueName">Nome della lista</param>
/// <param name="value">Valore da rimuovere</param>
/// <returns>Numero di elementi rimossi</returns>
public long QueueRemove(RedisKey queueName, RedisValue value)
{
// count = 0 → rimuove TUTTE le occorrenze del valore
return _db.ListRemove(queueName, value, 0);
}
/// <summary>
/// Rimuove un valore specifico dalla queue Redis (FIFO)
/// </summary>
/// <param name="queueName">Nome della lista</param>
/// <param name="value">Valore da rimuovere</param>
/// <returns>Numero di elementi rimossi</returns>
public async Task<long> QueueRemoveAsync(RedisKey queueName, RedisValue value)
{
// count = 0 → rimuove TUTTE le occorrenze del valore
return await _db.ListRemoveAsync(queueName, value, 0);
}
/// <summary>
/// Reset della coda
/// </summary>
/// <param name="queueName"></param>
/// <returns></returns>
public bool QueueReset(RedisKey queueName)
{
bool answ = _db.KeyDelete(queueName);
return answ;
}
/// <summary>
/// Reset della coda
/// </summary>
/// <param name="queueName"></param>
/// <returns></returns>
public async Task<bool> QueueResetAsync(RedisKey queueName)
{
bool answ = await _db.KeyDeleteAsync(queueName);
return answ;
}
/// <summary>
/// Scrittura string su cache REDIS in modalità Async
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expiry"></param>
/// <returns></returns>
public bool Set(string key, string value, TimeSpan? expiry = null)
{
Log.Trace($"Set request for {key}");
return _db.StringSet(key, value, expiry);
}
/// <summary>
/// Scrittura string su cache REDIS in modalità Async
/// </summary>
/// <param name="key"></param>
/// <param name="value"></param>
/// <param name="expiry"></param>
/// <returns></returns>
public async Task<bool> SetAsync(string key, string value, TimeSpan? expiry = null)
{
Log.Trace($"SetAsync request for {key}");
return await _db.StringSetAsync(key, value, expiry);
}
/// <summary>
/// Sottoscrizione canale REDIS + handler esecuzione
/// </summary>
/// <param name="channel"></param>
/// <param name="handler"></param>
public void Subscribe(string channel, Action<RedisChannel, RedisValue> 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
/// <summary>
/// conf speciale serializzatore JSON
/// </summary>
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
}
}