Ancora update metodi SPEC
This commit is contained in:
@@ -401,7 +401,44 @@ namespace MP.Data.Controllers
|
||||
.CountAsync();
|
||||
return result;
|
||||
}
|
||||
/// <summary>
|
||||
/// Conteggio articoli data condizione ricerca
|
||||
/// </summary>
|
||||
/// <param name="tipoArt"></param>
|
||||
/// <param name="azienda"></param>
|
||||
/// <param name="searchVal"></param>
|
||||
/// <returns></returns>
|
||||
public async Task<int> ArticoliCountSearchAsync(string tipoArt = "*", string azienda = "*", string searchVal = "")
|
||||
{
|
||||
using var dbCtx = new MoonProContext(options);
|
||||
IQueryable<AnagArticoliModel> query = dbCtx.DbSetArticoli.AsNoTracking();
|
||||
|
||||
// filtro tipo articolo
|
||||
if (tipoArt != "*")
|
||||
{
|
||||
//query = query.Where(x => x.Tipo.ToLower() == tipoArt.ToLower());
|
||||
query = query.Where(x => EF.Functions.Like(x.Tipo, tipoArt));
|
||||
}
|
||||
// filtro azienda
|
||||
if (azienda != "*")
|
||||
{
|
||||
//query = query.Where(x => x.Azienda.ToLower() == azienda.ToLower());
|
||||
query = query.Where(x => EF.Functions.Like(x.Azienda, azienda));
|
||||
}
|
||||
// filtro ricerca
|
||||
if (!string.IsNullOrWhiteSpace(searchVal))
|
||||
{
|
||||
string pattern = $"%{searchVal}%";
|
||||
query = query.Where(x =>
|
||||
EF.Functions.Like(x.CodArticolo, pattern) ||
|
||||
EF.Functions.Like(x.DescArticolo, pattern) ||
|
||||
EF.Functions.Like(x.Disegno, pattern));
|
||||
}
|
||||
|
||||
return await query
|
||||
.OrderBy(x => x.CodArticolo)
|
||||
.CountAsync();
|
||||
}
|
||||
/// <summary>
|
||||
/// Elenco tabella Articoli IMPIEGATI (da stored stp_ART_getUsed) Async
|
||||
/// </summary>
|
||||
@@ -499,10 +536,6 @@ namespace MP.Data.Controllers
|
||||
// filtro ricerca
|
||||
if (!string.IsNullOrWhiteSpace(searchVal))
|
||||
{
|
||||
//query = query.Where(x =>
|
||||
// x.CodArticolo.Contains(searchVal) ||
|
||||
// x.DescArticolo.Contains(searchVal) ||
|
||||
// x.Disegno.Contains(searchVal));
|
||||
string pattern = $"%{searchVal}%";
|
||||
query = query.Where(x =>
|
||||
EF.Functions.Like(x.CodArticolo, pattern) ||
|
||||
@@ -651,16 +684,12 @@ namespace MP.Data.Controllers
|
||||
/// <returns></returns>
|
||||
public async Task<List<ConfigModel>> ConfigGetAllAsync()
|
||||
{
|
||||
List<ConfigModel> dbResult = new List<ConfigModel>();
|
||||
using (var dbCtx = new MoonProContext(options))
|
||||
{
|
||||
dbResult = await dbCtx
|
||||
.DbSetConfig
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.Chiave)
|
||||
.ToListAsync();
|
||||
}
|
||||
return dbResult;
|
||||
using var dbCtx = new MoonProContext(options);
|
||||
return await dbCtx
|
||||
.DbSetConfig
|
||||
.AsNoTracking()
|
||||
.OrderBy(x => x.Chiave)
|
||||
.ToListAsync() ?? new();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -224,7 +224,7 @@ namespace MP.SPEC.Components
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await MDService.ConfigResetCache();
|
||||
await MDService.ConfigResetCacheAsync();
|
||||
ListGruppiFase = await MDService.ElencoGruppiFaseAsync();
|
||||
ListStati = await MDService.AnagStatiCommAsync();
|
||||
selAzienda = await MDService.ConfigTryGetAsync("AZIENDA");
|
||||
|
||||
@@ -171,7 +171,7 @@ namespace MP.SPEC.Components
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
SelFilter = new SelectFluxParams();
|
||||
await MDService.ConfigResetCache();
|
||||
await MDService.ConfigResetCacheAsync();
|
||||
var result = await MDService.ConfigTryGetAsync("SPEC_ParamTempoAgg");
|
||||
if (result != null)
|
||||
{
|
||||
|
||||
+156
-251
@@ -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>
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>MP.SPEC</RootNamespace>
|
||||
<Version>8.16.2605.2819</Version>
|
||||
<Version>8.16.2605.2907</Version>
|
||||
<UserSecretsId>1800a78a-6ff1-40f9-b490-87fb8bfc1394</UserSecretsId>
|
||||
<SatelliteResourceLanguages>en</SatelliteResourceLanguages>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -19,8 +19,8 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="px-0 align-content-center">
|
||||
<div class="input-group">
|
||||
<div class="px-0 align-items-center d-flex justify-content-end gap-1">
|
||||
<div class="input-group flex-grow-1">
|
||||
<span class="input-group-text"><i class="fa fa-rectangle-list"></i></span>
|
||||
<select @bind="@selTipoArt" class="form-select form-select-sm" style="width: 8rem; flex: 0 0 auto;" title="Selezionare il tipo di Articolo da mostrare" @bind:after="ResetDataAsync">
|
||||
<option value="*">--- Tutti ---</option>
|
||||
@@ -33,10 +33,12 @@
|
||||
}
|
||||
</select>
|
||||
<span class="input-group-text"><i class="fa fa-search"></i></span>
|
||||
<input type="text" class="form-control form-control-sm" placeholder="Cerca (3+ char)" aria-label="Ricerca" title="Ricerca (3+ char)" @bind="@SearchVal" @bind:after="ResetDataAsync">
|
||||
<input type="text" class="form-control form-control-sm" placeholder="Cerca (3+ char)" aria-label="Ricerca" title="Ricerca (3+ char)" @bind="@SearchVal" @bind:after="ResetDataAsync" >
|
||||
<button class="btn @searchCss" @onclick="() => ResetSearch()"><i class="fa fa-ban"></i></button>
|
||||
<span class="input-group-text"><i class="fa fa-database"></i></span>
|
||||
<input type="number" inputmode="numeric" min="10" max="100000" class="form-control form-control-sm text-end" style="width: 6rem; flex: 0 0 auto;" aria-label="Num record" title="Num massimo record da recuperare" @bind="@maxNumRecord" @bind:after="ReloadDataAsync">
|
||||
<label class="input-group-text" for="maxRecord" title="Selezionare l'azienda da visualizzare"><i class="fa-solid fa-industry"></i></label>
|
||||
<select @bind="@selAzienda" class="form-select form-select-sm" style="width: 15rem; flex: 0 0 auto;" title="Selezionare l'azienda da visualizzare" @bind:after="ReloadAziendaAsync">
|
||||
<select @bind="@selAzienda" class="form-select" style="width: 15rem; flex: 0 0 auto;" title="Selezionare l'azienda da visualizzare" @bind:after="ReloadAziendaAsync">
|
||||
@if (ListAziende != null)
|
||||
{
|
||||
foreach (var item in ListAziende)
|
||||
@@ -45,8 +47,7 @@
|
||||
}
|
||||
}
|
||||
</select>
|
||||
<span class="input-group-text"><i class="fa fa-database"></i></span>
|
||||
<input type="number" inputmode="numeric" min="10" max="100000" class="form-control form-control-sm text-end" style="width: 6rem; flex: 0 0 auto;" aria-label="Num record" title="Num massimo record da recuperare" @bind="@maxNumRecord" @bind:after="ReloadDataAsync">
|
||||
<span class="input-group-text p-1"><small>#: @availRecord</small></span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -190,7 +190,9 @@ namespace MP.SPEC.Pages
|
||||
private List<AnagGruppiModel>? ListAziende;
|
||||
private List<AnagArticoliModel>? ListRecords;
|
||||
private List<ListValuesModel>? ListTipoArt;
|
||||
private int maxNumRecord = 5000;
|
||||
private int maxNumRecord = 1000;
|
||||
private int availRecord = 1000;
|
||||
private int totRecord = 0;
|
||||
private List<AnagArticoliModel>? SearchRecords;
|
||||
|
||||
#endregion Private Fields
|
||||
@@ -256,7 +258,7 @@ namespace MP.SPEC.Pages
|
||||
Valore = selAzienda
|
||||
};
|
||||
await MDService.ConfigUpdateAsync(updRec);
|
||||
await MDService.ConfigResetCache();
|
||||
await MDService.ConfigResetCacheAsync();
|
||||
// ricarico
|
||||
await ResetDataAsync();
|
||||
}
|
||||
@@ -302,6 +304,7 @@ namespace MP.SPEC.Pages
|
||||
{
|
||||
isLoading = true;
|
||||
SearchRecords = await MDService.ArticoliGetSearchAsync(maxNumRecord, selTipoArt, selAzienda, SearchVal);
|
||||
availRecord = await MDService.ArticoliCountSearchAsync(selTipoArt, selAzienda, searchVal);
|
||||
totalCount = SearchRecords.Count;
|
||||
UpdateTable();
|
||||
}
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace MP.SPEC.Pages
|
||||
|
||||
protected override async Task OnInitializedAsync()
|
||||
{
|
||||
await MDService.ConfigResetCache();
|
||||
await MDService.ConfigResetCacheAsync();
|
||||
giacenzeConf = await MDService.ConfigTryGetAsync("SPEC_ShowGiacenze");
|
||||
await Task.Delay(1);
|
||||
padCodXdl = await MDService.ConfigTryGetAsync("PadCodXdl");
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using EgwCoreLib.Razor;
|
||||
using Microsoft.AspNetCore.Components;
|
||||
using MP.Data.DbModels;
|
||||
using MP.SPEC.Components;
|
||||
using MP.SPEC.Data;
|
||||
using NLog;
|
||||
using EgwCoreLib.Razor;
|
||||
|
||||
namespace MP.SPEC.Pages
|
||||
{
|
||||
@@ -23,7 +22,7 @@ namespace MP.SPEC.Pages
|
||||
public void ElapsedTimer(object? source, System.Timers.ElapsedEventArgs e)
|
||||
{
|
||||
// controllo se sia scaduto tempo massimo (in redis) x ricaricare pagina in modo completo...
|
||||
var dtRif = MDService.ExpiryReloadParamGet();
|
||||
var dtRif = MDService.DtParamExpiry;
|
||||
if (dtRif > DateTime.Now)
|
||||
{
|
||||
Log.Trace("----- Elapsed check PARAMS.cs -----");
|
||||
@@ -32,7 +31,7 @@ namespace MP.SPEC.Pages
|
||||
{
|
||||
var pUpd = Task.Run(async () =>
|
||||
{
|
||||
MDService.ExpiryReloadParamSet(DateTime.Now.AddSeconds(intForceReload));
|
||||
MDService.DtParamExpiry = DateTime.Now.AddSeconds(intForceReload);
|
||||
aTimer.Elapsed -= ElapsedTimer;
|
||||
aTimer.Stop();
|
||||
aTimer.Close();
|
||||
@@ -91,7 +90,6 @@ namespace MP.SPEC.Pages
|
||||
{
|
||||
updFilter.lastUpdate = updFilter.lastUpdate == "-" ? $"{adesso:yyyy/MM/dd HH:mm:ss}" : updFilter.lastUpdate;
|
||||
updFilter.IdxMacchina = newRec.IdxMacchina;
|
||||
//updFilter.CodFlux = newRec.CodFlux;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -124,7 +122,6 @@ namespace MP.SPEC.Pages
|
||||
currPage = newNum;
|
||||
DateTime adesso = DateTime.Now.AddSeconds(1);
|
||||
var updFilter = currFilter;
|
||||
//updFilter.LiveUpdate = (currPage == 1);
|
||||
updFilter.LiveUpdate = (currFilter.CurrPage == 1);
|
||||
updFilter.lastUpdate = updFilter.LiveUpdate ? "-" : $"{adesso:yyyy/MM/dd HH:mm:ss}";
|
||||
// salvo filtro
|
||||
@@ -211,10 +208,10 @@ namespace MP.SPEC.Pages
|
||||
private void setExpiryReload()
|
||||
{
|
||||
// verifico se ho una scadenza expiry del periodo desiderato, sennò imposto nuova...
|
||||
var dtRif = MDService.ExpiryReloadParamGet();
|
||||
var dtRif = MDService.DtParamExpiry;
|
||||
if (dtRif <= DateTime.Now)
|
||||
{
|
||||
MDService.ExpiryReloadParamSet(DateTime.Now.AddSeconds(intForceReload));
|
||||
MDService.DtParamExpiry = DateTime.Now.AddSeconds(intForceReload);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -222,18 +219,11 @@ namespace MP.SPEC.Pages
|
||||
{
|
||||
isFiltering = false;
|
||||
isLoading = true;
|
||||
await Task.Delay(1);
|
||||
currPage = 1;
|
||||
if (newParams.CurrPage == 0)
|
||||
{
|
||||
newParams.CurrPage = 1;
|
||||
//newParams.LiveUpdate = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
//newParams.LiveUpdate = (currPage == 1);
|
||||
}
|
||||
await Task.Delay(1);
|
||||
await InvokeAsync(() => StateHasChanged());
|
||||
currFilter = newParams;
|
||||
isLoading = false;
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<body>
|
||||
<i>Modulo MAPOSPEC </i>
|
||||
<h4>Versione: 8.16.2605.2819</h4>
|
||||
<h4>Versione: 8.16.2605.2907</h4>
|
||||
<br /> Note di rilascio:
|
||||
<ul>
|
||||
<li>
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.16.2605.2819
|
||||
8.16.2605.2907
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<item>
|
||||
<version>8.16.2605.2819</version>
|
||||
<version>8.16.2605.2907</version>
|
||||
<url>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip</url>
|
||||
<changelog>https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html</changelog>
|
||||
<mandatory>false</mandatory>
|
||||
|
||||
Reference in New Issue
Block a user