Ancora update metodi SPEC

This commit is contained in:
Samuele Locatelli
2026-05-29 07:23:54 +02:00
parent ff36dadadc
commit 116d7395c9
12 changed files with 224 additions and 296 deletions
+156 -251
View File
@@ -90,6 +90,15 @@ namespace MP.SPEC.Data
public MessagePipe BroadastMsgPipe { get; set; } = null!;
public Dictionary<string, List<TagData>> currTagConf { get; set; } = new Dictionary<string, List<TagData>>();
/// <summary>
/// Expiry DateTime x refresh pagina parametri
/// </summary>
public DateTime DtParamExpiry
{
get => _dtParamExpiry;
set => _dtParamExpiry = value;
}
#endregion Public Properties
#region Public Methods
@@ -252,6 +261,33 @@ namespace MP.SPEC.Data
);
}
public async Task<int> ArticoliCountAsync()
{
string redisKey = $"{Utils.redisArtList}:Count";
return await GetOrFetchAsync(
operationName: "ArticoliCountAsync",
cacheKey: redisKey,
expiration: getRandTOut(redisLongTimeCache),
fetchFunc: async () =>
await dbController.ArticoliCountAsync(),
tagList: [Utils.redisArtList, $"{Utils.redisArtList}:CountAll"]
);
}
public async Task<int> ArticoliCountSearchAsync(string tipo = "*", string azienda = "*", string searchVal = "")
{
string sKey = string.IsNullOrWhiteSpace(tipo) ? "ALL" : tipo.Trim();
string redisKey = $"{Utils.redisArtList}:{azienda}:{sKey}:{searchVal}:Count";
return await GetOrFetchAsync(
operationName: "ArticoliCountSearchAsync",
cacheKey: redisKey,
expiration: getRandTOut(redisLongTimeCache),
fetchFunc: async () =>
await dbController.ArticoliCountSearchAsync(tipo, azienda, searchVal),
tagList: [Utils.redisArtList, $"{Utils.redisArtList}:CountSearch"]
);
}
/// <summary>
/// Eliminazione record selezionato
/// </summary>
@@ -278,7 +314,7 @@ namespace MP.SPEC.Data
public async Task<List<AnagArticoliModel>> ArticoliGetByTipoAsync(string tipo, string azienda = "*")
{
string sKey = string.IsNullOrWhiteSpace(tipo) ? "ALL" : tipo.Trim();
string redisKey = $"{Utils.redisArtList}:{azienda}:Tipo:{sKey}";
string redisKey = $"{Utils.redisArtList}:{azienda}:{sKey}";
return await GetOrFetchAsync(
operationName: "ArticoliGetByTipoAsync",
cacheKey: redisKey,
@@ -289,23 +325,6 @@ namespace MP.SPEC.Data
);
}
/// <summary>
/// Elenco articoli contenuti in Kit (come child), non eliminabli
/// </summary>
/// <returns></returns>
public async Task<List<AnagArticoliModel>> ArticoliInKitAsync()
{
string redisKey = $"{Utils.redisArtList}:InKit";
return await GetOrFetchAsync(
operationName: "ArticoliInKitAsync",
cacheKey: redisKey,
expiration: getRandTOut(redisLongTimeCache),
fetchFunc: async () =>
await dbController.ArticoliInKitAsync() ?? new List<AnagArticoliModel>(),
tagList: [Utils.redisArtList, $"{Utils.redisArtList}:InKit"]
);
}
/// <summary>
/// Restitusice elenco articoli cercati
/// </summary>
@@ -328,6 +347,23 @@ namespace MP.SPEC.Data
);
}
/// <summary>
/// Elenco articoli contenuti in Kit (come child), non eliminabli
/// </summary>
/// <returns></returns>
public async Task<List<AnagArticoliModel>> ArticoliInKitAsync()
{
string redisKey = $"{Utils.redisArtList}:InKit";
return await GetOrFetchAsync(
operationName: "ArticoliInKitAsync",
cacheKey: redisKey,
expiration: getRandTOut(redisLongTimeCache),
fetchFunc: async () =>
await dbController.ArticoliInKitAsync() ?? new List<AnagArticoliModel>(),
tagList: [Utils.redisArtList, $"{Utils.redisArtList}:InKit"]
);
}
/// <summary>
/// Aggiornamento record selezionato
/// </summary>
@@ -458,14 +494,14 @@ namespace MP.SPEC.Data
/// Reset dati cache config
/// </summary>
/// <returns></returns>
public async Task ConfigResetCache()
public async Task ConfigResetCacheAsync()
{
using var activity = ActivitySource.StartActivity("ConfigResetCache");
using var activity = ActivitySource.StartActivity("ConfigResetCacheAsync");
string source = "REDIS";
await ResetConfigCache();
activity?.SetTag("data.source", source);
activity?.Stop();
LogTrace($"ConfigResetCache Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
LogTrace($"ConfigResetCacheAsync Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
}
/// <summary>
@@ -696,7 +732,6 @@ namespace MP.SPEC.Data
);
}
/// <summary>
/// Elenco link validi per il menu
/// </summary>
@@ -901,47 +936,6 @@ namespace MP.SPEC.Data
return answ;
}
/// <summary>
/// Imposta in redis la scadenza della pagina x il reload
/// </summary>
/// <param name="expTime"></param>
/// <returns></returns>
public DateTime ExpiryReloadParamGet()
{
using var activity = ActivitySource.StartActivity("ExpiryReloadParamGet");
string source = "REDIS";
DateTime dtRif = DateTime.Now;
string currKey = $"{Utils.redisParamPageExp}";
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
dtRif = JsonConvert.DeserializeObject<DateTime>($"{rawData}");
}
activity?.SetTag("data.source", source);
activity?.Stop();
LogTrace($"ExpiryReloadParamGet | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return dtRif;
}
/// <summary>
/// Imposta in redis la scadenza della pagina x il reload
/// </summary>
/// <param name="expTime"></param>
/// <returns></returns>
public bool ExpiryReloadParamSet(DateTime expTime)
{
using var activity = ActivitySource.StartActivity("ExpiryReloadParamSet");
string source = "REDIS";
bool fatto = false;
string currKey = $"{Utils.redisParamPageExp}";
string rawData = JsonConvert.SerializeObject(expTime);
fatto = redisDb.StringSet(currKey, rawData);
activity?.SetTag("data.source", source);
activity?.Stop();
LogTrace($"ExpiryReloadParamSet | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return fatto;
}
/// <summary>
/// Cancellazione FusionCache (totale)
/// </summary>
@@ -1449,24 +1443,6 @@ namespace MP.SPEC.Data
);
}
/// <summary>
/// Elenco operatori filtrati x gruppo
/// </summary>
/// <param name="codGruppo"></param>
/// <returns></returns>
public async Task<List<AnagOperatoriModel>> OperatoriGetFiltAsync(string codGruppo)
{
string keyGrp = codGruppo != "*" ? codGruppo : "ALL";
string currKey = $"{Utils.redisOprList}:{keyGrp}";
return await GetOrFetchAsync(
operationName: "OperatoriGetFiltAsync",
cacheKey: currKey,
expiration: getRandTOut(redisLongTimeCache),
fetchFunc: async () => await dbController.OperatoriGetFiltAsync(codGruppo) ?? new List<AnagOperatoriModel>(),
tagList: [Utils.redisOprList]
);
}
/// <summary>
/// Elenco id Macchine che abbiano dati FLuxLog, nel periodo indicato
/// </summary>
@@ -1534,33 +1510,6 @@ namespace MP.SPEC.Data
},
tagList: [Utils.redisIobConf]
);
#if false
using var activity = ActivitySource.StartActivity("MachIobConfAsync");
string source = "DB";
Dictionary<string, string> result = new Dictionary<string, string>();
// cerco in redis...
string currKey = redHashMpIO($"IOB:{IdxMacchina}:MachIobConfAsync");
try
{
result = (await redisDb.HashGetAllAsync(currKey))
.ToDictionary(x => $"{x.Name}", x => $"{x.Value}");
source = "REDIS";
}
catch (Exception exc)
{
Log.Error($"Errore in MachIobConfAsync{Environment.NewLine}{exc}");
}
if (result == null)
{
result = new Dictionary<string, string>();
LogTrace($"Init valore default MachIobConfAsync | IdxMacchina: {IdxMacchina}");
}
activity?.SetTag("data.source", source);
activity?.SetTag("result.count", result.Count);
activity?.Stop();
LogTrace($"MachIobConfAsync per {IdxMacchina} | {source} | {activity?.Duration.TotalMilliseconds}ms");
return result;
#endif
}
/// <summary>
@@ -1728,7 +1677,6 @@ namespace MP.SPEC.Data
/// <returns></returns>
public async Task<List<ODLExpModel>> OdlListGetFiltAsync(bool inCorso, string codArt, string keyRichPart, string Reparto, string IdxMacchina, DateTime startDate, DateTime endDate)
{
string currKey = $"{Utils.redisOdlList}:{inCorso}:{codArt}:{keyRichPart}:{Reparto}:{IdxMacchina}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}";
return await GetOrFetchAsync(
operationName: "OdlListGetFiltAsync",
@@ -1737,36 +1685,25 @@ namespace MP.SPEC.Data
fetchFunc: async () => await dbController.ListODLFiltAsync(inCorso, codArt, keyRichPart, Reparto, IdxMacchina, startDate, endDate) ?? new(),
tagList: [Utils.redisOdlList]
);
}
#if false
using var activity = ActivitySource.StartActivity("OdlListGetFiltAsync");
List<ODLExpModel>? result = new List<ODLExpModel>();
string source = "DB";
string currKey = $"{Utils.redisOdlList}:{inCorso}:{codArt}:{keyRichPart}:{Reparto}:{IdxMacchina}:{startDate:yyyyMMdd_HHmmss}:{endDate:yyyyMMdd_HHmmss}";
// cerco in redis dato valore sel idxMaccSel...
RedisValue rawData = redisDb.StringGet(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<List<ODLExpModel>>($"{rawData}");
source = "REDIS";
}
else
{
result = await dbController.ListODLFiltAsync(inCorso, codArt, keyRichPart, Reparto, IdxMacchina, startDate, endDate);
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
redisDb.StringSet(currKey, rawData, TimeSpan.FromSeconds(redisShortTimeCache));
}
if (result == null)
{
result = new List<ODLExpModel>();
}
activity?.SetTag("data.source", source);
activity?.SetTag("result.count", result.Count);
activity?.Stop();
LogTrace($"OdlListGetFiltAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms");
return result;
#endif
/// <summary>
/// Elenco operatori filtrati x gruppo
/// </summary>
/// <param name="codGruppo"></param>
/// <returns></returns>
public async Task<List<AnagOperatoriModel>> OperatoriGetFiltAsync(string codGruppo)
{
string keyGrp = codGruppo != "*" ? codGruppo : "ALL";
string currKey = $"{Utils.redisOprList}:{keyGrp}";
return await GetOrFetchAsync(
operationName: "OperatoriGetFiltAsync",
cacheKey: currKey,
expiration: getRandTOut(redisLongTimeCache),
fetchFunc: async () => await dbController.OperatoriGetFiltAsync(codGruppo) ?? new List<AnagOperatoriModel>(),
tagList: [Utils.redisOprList]
);
}
/// <summary>
@@ -1786,6 +1723,75 @@ namespace MP.SPEC.Data
);
}
/// <summary>
/// Restituisce dizionario ODL/PODL data lista IdxOdl
/// </summary>
/// <param name="idxOdlList"></param>
/// <returns></returns>
public async Task<Dictionary<int, int>> PODL_getDictOdlPodlAsync(List<int> idxOdlList)
{
if (idxOdlList == null || !idxOdlList.Any())
return new Dictionary<int, int>();
var distinctIds = idxOdlList.Distinct().ToList();
var resultDictionary = new Dictionary<int, int>();
var missingIds = new List<int>();
// STEP 1: Controllo rapido in FusionCache (L1/Memory cache)
foreach (var id in distinctIds)
{
var cacheKey = $"val:{id}";
var cachedValue = await _cache.TryGetAsync<int>(cacheKey);
if (cachedValue.HasValue)
{
resultDictionary[id] = cachedValue.Value;
}
else
{
// ID non presente in cache, andrà cercato tramite il servizio EF
missingIds.Add(id);
}
}
// STEP 2: Se ci sono cache miss, interroghiamo il servizio EF Core
if (missingIds.Any())
{
// Riceviamo direttamente un Dictionary<int, int> ottimizzato da EF Core
Dictionary<int, int> dbResults = await dbController.PODL_getDictOdlPodlAsync(missingIds);
// STEP 3: Scriviamo i risultati in cache e li uniamo al dizionario finale
foreach (var kvp in dbResults)
{
var id = kvp.Key;
var targetValue = kvp.Value;
resultDictionary[id] = targetValue;
// Salvataggio atomico e globale su FusionCache
var cacheKey = $"val:{id}";
await _cache.SetAsync(cacheKey, targetValue, TimeSpan.FromMinutes(30));
}
// STEP 4 [Altamente Consigliato]: Cache Penetration Protection
// Se un ID era tra i "missing" ma NON è presente nei risultati del DB, significa che non esiste.
// Salviamo un valore sentinella (es. 0 o -1) per evitare di ricontrollare il DB al prossimo giro.
foreach (var id in missingIds)
{
if (!dbResults.ContainsKey(id))
{
resultDictionary[id] = 0; // Imposta un default per l'output corrente
var cacheKey = $"val:{id}";
await _cache.SetAsync(cacheKey, 0, TimeSpan.FromMinutes(2)); // Scadenza breve per i record inesistenti
}
}
}
return resultDictionary;
}
/// <summary>
/// Eliminazione record selezionato
/// </summary>
@@ -1886,75 +1892,6 @@ namespace MP.SPEC.Data
);
}
/// <summary>
/// Restituisce dizionario ODL/PODL data lista IdxOdl
/// </summary>
/// <param name="idxOdlList"></param>
/// <returns></returns>
public async Task<Dictionary<int, int>> PODL_getDictOdlPodlAsync(List<int> idxOdlList)
{
if (idxOdlList == null || !idxOdlList.Any())
return new Dictionary<int, int>();
var distinctIds = idxOdlList.Distinct().ToList();
var resultDictionary = new Dictionary<int, int>();
var missingIds = new List<int>();
// STEP 1: Controllo rapido in FusionCache (L1/Memory cache)
foreach (var id in distinctIds)
{
var cacheKey = $"val:{id}";
var cachedValue = await _cache.TryGetAsync<int>(cacheKey);
if (cachedValue.HasValue)
{
resultDictionary[id] = cachedValue.Value;
}
else
{
// ID non presente in cache, andrà cercato tramite il servizio EF
missingIds.Add(id);
}
}
// STEP 2: Se ci sono cache miss, interroghiamo il servizio EF Core
if (missingIds.Any())
{
// Riceviamo direttamente un Dictionary<int, int> ottimizzato da EF Core
Dictionary<int, int> dbResults = await dbController.PODL_getDictOdlPodlAsync(missingIds);
// STEP 3: Scriviamo i risultati in cache e li uniamo al dizionario finale
foreach (var kvp in dbResults)
{
var id = kvp.Key;
var targetValue = kvp.Value;
resultDictionary[id] = targetValue;
// Salvataggio atomico e globale su FusionCache
var cacheKey = $"val:{id}";
await _cache.SetAsync(cacheKey, targetValue, TimeSpan.FromMinutes(30));
}
// STEP 4 [Altamente Consigliato]: Cache Penetration Protection
// Se un ID era tra i "missing" ma NON è presente nei risultati del DB, significa che non esiste.
// Salviamo un valore sentinella (es. 0 o -1) per evitare di ricontrollare il DB al prossimo giro.
foreach (var id in missingIds)
{
if (!dbResults.ContainsKey(id))
{
resultDictionary[id] = 0; // Imposta un default per l'output corrente
var cacheKey = $"val:{id}";
await _cache.SetAsync(cacheKey, 0, TimeSpan.FromMinutes(2)); // Scadenza breve per i record inesistenti
}
}
}
return resultDictionary;
}
/// <summary>
/// Effettua il task di eliminazione PODL KIT + istanze + riattivazione PODL originali disattivate tramite stored
/// </summary>
@@ -2281,35 +2218,6 @@ namespace MP.SPEC.Data
fetchFunc: async () => await dbController.StatoMacchinaAsync(idxMacchina) ?? new(),
tagList: [Utils.redisStatoMacch]
);
#if false
using var activity = ActivitySource.StartActivity("StatoMacchinaAsync");
// setup parametri costanti
string source = "DB";
StatoMacchineModel? result = new StatoMacchineModel();
// cerco in redisConn...
string currKey = $"{Utils.redisStatoMacch}:{idxMacchina}";
RedisValue rawData = await redisDb.StringGetAsync(currKey);
if (rawData.HasValue)
{
result = JsonConvert.DeserializeObject<StatoMacchineModel>($"{rawData}");
source = "REDIS";
}
else
{
result = dbController.StatoMacchina(idxMacchina);
// serializzo e salvo...
rawData = JsonConvert.SerializeObject(result);
await redisDb.StringSetAsync(currKey, rawData, TimeSpan.FromSeconds(1));
}
if (result == null)
{
result = new StatoMacchineModel();
}
activity?.SetTag("data.source", source);
activity?.Stop();
LogTrace($"StatoMacchinaAsync | {source} | {activity?.Duration.TotalMilliseconds}ms");
return result;
#endif
}
/// <summary>
@@ -2385,7 +2293,7 @@ namespace MP.SPEC.Data
if (ForceDb)
{
// Se ForceDb è true, saltiamo il GetOrFetchAsync per forzare il fetch dal DB
// Se ForceDb è true, saltiamo il GetOrFetchAsync per forzare il fetch dal DB
// e aggiornare la cache.
var result = await dbController.TksScoreAsync(KeyFilt, MaxResult) ?? new List<TksScoreModel>();
await _cache.SetAsync(currKey, result, TimeSpan.FromMinutes(redisLongTimeCache), tags: [Utils.redisKitScore]);
@@ -2620,14 +2528,16 @@ namespace MP.SPEC.Data
private static readonly ActivitySource ActivitySource = new ActivitySource("MP.DATA.Tracer");
private static IConfiguration _configuration = null!;
private static Logger Log = LogManager.GetCurrentClassLogger();
private readonly IFusionCache _cache;
private DateTime _artCacheExpiry = DateTime.MinValue;
private Dictionary<string, string> _configData = new();
private DateTime _dtParamExpiry = DateTime.Now;
/// <summary>
/// Elenco CodArticolo usati in istanza KIT (per verifica eliminabilità)
/// </summary>
private HashSet<string> _listCodArtInKit = new();
/// <summary>
/// Elenco CodArticolo NON usati (per verifica eliminabilità)
@@ -2639,11 +2549,6 @@ namespace MP.SPEC.Data
/// </summary>
private HashSet<string> _listCodArtUsed = new();
/// <summary>
/// Elenco CodArticolo usati in istanza KIT (per verifica eliminabilità)
/// </summary>
private HashSet<string> _listCodArtInKit = new();
private string canCacheParametri = "";
private string MpIoNS = "";
@@ -2680,6 +2585,11 @@ namespace MP.SPEC.Data
/// </summary>
private int redisShortTimeCache = 5;
/// <summary>
/// Soglia minima (ms) per log timing in console
/// </summary>
private double slowLogThresh = 0;
private bool traceEnabled = false;
#endregion Private Fields
@@ -2722,11 +2632,6 @@ namespace MP.SPEC.Data
}
}
/// <summary>
/// Soglia minima (ms) per log timing in console
/// </summary>
private double slowLogThresh = 0;
/// <summary>
/// Implementa gestione recupero cache da memoria o da obj esterno + cache memoria + tracking attività
/// </summary>