From 1eb51852405f1ba12016a2c699332aadc312ae99 Mon Sep 17 00:00:00 2001 From: Samuele Locatelli Date: Wed, 27 May 2026 09:46:55 +0200 Subject: [PATCH] Continuo refactor con cache Fusion --- MP.Data/Controllers/MpSpecController.cs | 24 +++++++++- MP.SPEC/Components/CmpTop.razor.cs | 4 +- MP.SPEC/Data/MpDataService.cs | 13 +++-- MP.SPEC/MP.SPEC.csproj | 2 +- MP.SPEC/Pages/Articoli.razor.cs | 2 +- MP.SPEC/Pages/PODL.razor.cs | 2 +- MP.SPEC/Resources/ChangeLog.html | 2 +- MP.SPEC/Resources/VersNum.txt | 2 +- MP.SPEC/Resources/manifest.xml | 2 +- refactor_spec.md | 63 +++++++++++++++++++++++++ 10 files changed, 100 insertions(+), 16 deletions(-) create mode 100644 refactor_spec.md diff --git a/MP.Data/Controllers/MpSpecController.cs b/MP.Data/Controllers/MpSpecController.cs index cb8d41ef..5c39a619 100644 --- a/MP.Data/Controllers/MpSpecController.cs +++ b/MP.Data/Controllers/MpSpecController.cs @@ -164,9 +164,9 @@ namespace MP.Data.Controllers /// Elenco Gruppi tipo Azienda /// /// - public List AnagGruppiAziende() + public Task> AnagGruppiAziendeAsync() { - return AnagGruppiGetTipo("AZIENDA"); + return AnagGruppiGetTipoAsync("AZIENDA"); } /// @@ -241,6 +241,26 @@ namespace MP.Data.Controllers return dbResult; } + /// + /// Gruppi x tipo modalità Async + /// + /// + /// + public async Task> AnagGruppiGetTipoAsync(string tipoGruppo) + { + List dbResult = new List(); + using (var dbCtx = new MoonProContext(options)) + { + dbResult = await dbCtx + .DbSetAnagGruppi + .Where(x => x.TipoGruppo == tipoGruppo) + .AsNoTracking() + .OrderBy(x => x.CodGruppo) + .ToListAsync(); + } + return dbResult; + } + /// /// Elenco Gruppi tipo REPARTO (x associazione Macchine-Operatori) /// diff --git a/MP.SPEC/Components/CmpTop.razor.cs b/MP.SPEC/Components/CmpTop.razor.cs index c7d26602..593b6a6a 100644 --- a/MP.SPEC/Components/CmpTop.razor.cs +++ b/MP.SPEC/Components/CmpTop.razor.cs @@ -1,12 +1,9 @@ -using Amazon.Runtime.Internal.Endpoints.StandardLibrary; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Authorization; -using Microsoft.AspNetCore.Mvc.RazorPages; using Microsoft.JSInterop; using MP.AppAuth.Services; using MP.SPEC.Data; using MP.SPEC.Extensions; -using System; namespace MP.SPEC.Components { @@ -56,6 +53,7 @@ namespace MP.SPEC.Components protected async Task FlushCache() { await MDService.FlushRedisCache(); + await MDService.FlushCacheAsync(); await ForceReload(); // rimando a pagina corrente NavManager.NavigateTo(NavManager.Uri, true); diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs index 8370b7cf..6cc2b305 100644 --- a/MP.SPEC/Data/MpDataService.cs +++ b/MP.SPEC/Data/MpDataService.cs @@ -306,7 +306,7 @@ namespace MP.SPEC.Data return await GetOrFetchAsync( operationName: "AnagStatiCommAsync", cacheKey: Utils.redisStatoCom, - expiration: TimeSpan.FromMinutes(5), + expiration: TimeSpan.FromMinutes(redisLongTimeCache), fetchFunc: async () => await dbController.AnagStatiCommAsync() ?? new List() ); @@ -633,6 +633,7 @@ namespace MP.SPEC.Data { bool fatto = false; await _cache.ClearAsync(); + _configData.Clear(); fatto = true; return fatto; } @@ -648,6 +649,7 @@ namespace MP.SPEC.Data { await _cache.RemoveByTagAsync(item); } + _configData.Clear(); fatto = true; return fatto; } @@ -659,6 +661,7 @@ namespace MP.SPEC.Data { bool fatto = false; await _cache.RemoveByTagAsync(tag); + _configData.Clear(); fatto = true; return fatto; } @@ -1198,14 +1201,14 @@ namespace MP.SPEC.Data /// Restitusice elenco aziende /// /// - public List ElencoAziende() + public async Task> ElencoAziendeAsync() { - using var activity = ActivitySource.StartActivity("ElencoAziende"); + using var activity = ActivitySource.StartActivity("ElencoAziendeAsync"); string source = "DB"; - var listAz = dbController.AnagGruppiAziende(); + var listAz = await dbController.AnagGruppiAziendeAsync(); activity?.SetTag("data.source", source); activity?.Stop(); - LogTrace($"ElencoAziende | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); + LogTrace($"ElencoAziendeAsync | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return listAz; } diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj index 66b09440..26cb8e72 100644 --- a/MP.SPEC/MP.SPEC.csproj +++ b/MP.SPEC/MP.SPEC.csproj @@ -5,7 +5,7 @@ enable enable MP.SPEC - 8.16.2605.2708 + 8.16.2605.2709 1800a78a-6ff1-40f9-b490-87fb8bfc1394 en diff --git a/MP.SPEC/Pages/Articoli.razor.cs b/MP.SPEC/Pages/Articoli.razor.cs index 008bbc19..accea5f3 100644 --- a/MP.SPEC/Pages/Articoli.razor.cs +++ b/MP.SPEC/Pages/Articoli.razor.cs @@ -176,7 +176,7 @@ namespace MP.SPEC.Pages { selAzienda = "*"; } - ListAziende = MDService.ElencoAziende(); + ListAziende = await MDService.ElencoAziendeAsync(); ListTipoArt = await MDService.AnagTipoArtLV(); } diff --git a/MP.SPEC/Pages/PODL.razor.cs b/MP.SPEC/Pages/PODL.razor.cs index 23054a1a..aaf0d96c 100644 --- a/MP.SPEC/Pages/PODL.razor.cs +++ b/MP.SPEC/Pages/PODL.razor.cs @@ -129,7 +129,7 @@ namespace MP.SPEC.Pages protected override async Task OnInitializedAsync() { await getReparto(); - ListAziende = MDService.ElencoAziende(); + ListAziende = await MDService.ElencoAziendeAsync(); var allGruppiData = MDService.ElencoGruppiFase(); if (allGruppiData != null) { diff --git a/MP.SPEC/Resources/ChangeLog.html b/MP.SPEC/Resources/ChangeLog.html index 59e7e6d9..ff33fc09 100644 --- a/MP.SPEC/Resources/ChangeLog.html +++ b/MP.SPEC/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo MAPOSPEC -

Versione: 8.16.2605.2708

+

Versione: 8.16.2605.2709


Note di rilascio:
  • diff --git a/MP.SPEC/Resources/VersNum.txt b/MP.SPEC/Resources/VersNum.txt index 933735f6..0dfa7efa 100644 --- a/MP.SPEC/Resources/VersNum.txt +++ b/MP.SPEC/Resources/VersNum.txt @@ -1 +1 @@ -8.16.2605.2708 +8.16.2605.2709 diff --git a/MP.SPEC/Resources/manifest.xml b/MP.SPEC/Resources/manifest.xml index 02e7a70d..dcbc0eb7 100644 --- a/MP.SPEC/Resources/manifest.xml +++ b/MP.SPEC/Resources/manifest.xml @@ -1,6 +1,6 @@ - 8.16.2605.2708 + 8.16.2605.2709 https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/MP.SPEC.zip https://nexus.steamware.net/repository/SWS/MP-SPEC/stable/LAST/ChangeLog.html false diff --git a/refactor_spec.md b/refactor_spec.md new file mode 100644 index 00000000..2b8a587c --- /dev/null +++ b/refactor_spec.md @@ -0,0 +1,63 @@ +# Refactoring Plan: MpDataService Cache Layer Upgrade + +## Objective +Migrate all data access methods in `MpDataService.cs` to use the unified multi-level cache stack (IMemoryCache via `IFusionCache` + Redis + DB) using the `GetOrFetchAsync` pattern. + +## Current State +- Some methods use a manual "Redis + DB" approach (checking `redisDb.StringGet`, deserializing, and then `StringSet` on miss). +- Some methods use the new `GetOrFetchAsync` pattern (Memory + Redis + DB). +- `GetOrFetchAsync` leverages `IFusionCache`, which handles the multi-level complexity and provides better resiliency (fail-safe). + +## Target Pattern: `GetOrFetchAsync` +All read methods should be refactored to: +1. Use `GetOrFetchAsync` to abstract cache management. +2. Define a `cacheKey` (typically matching the existing Redis key). +3. Provide an `expiration` (TimeSpan). +4. Provide a `fetchFunc` that calls the `dbController` method. + +## Proposed Work Items + +### 1. Analysis & Categorization +Identify all methods in `MpDataService.cs` that currently implement manual Redis caching. + +**Candidates for Refactoring (Redis + DB -> Multi-level):** +- `AnagEventiGeneral` (Lines 166-195) +- `AnagEventiGetByMacch` (Lines 201-230) +- `AnagKeyValGetAll` (Lines 273-302) +- `AnagTipoArtLV` (Lines 347-375) +- `ArticleWithDossier` (Lines 381-410) +- `ConfigGetAll` (Lines 907-936) +- `ConfigGetAllAsync` (Lines 943-970) +- `DbDedupStats` (Lines 1046-1067) +- `ElencoGruppiFase` (Lines 1216-1249) +- `ElencoRepartiDTO` (Lines 1267-1303) +- `FluxLogGetLastFilt` (Lines 1569-1605) +- `FluxLogPareto` (Lines 1609-1640) +- `MacchineRecipeArchive` (Lines 2100-2128) +- `MacchineRecipeConf` (Lines 2131-2163) +- `MacchineWithFlux` (Lines 2171-2200) +- `OdlByBatch` (Lines 2308-2336) +- `OdlListAll` (Lines 2490-2499) - *Currently no cache, needs addition?* +- `OdlListGetFilt` (Lines 2513-2542) +- `OperatoriGetFilt` (Lines 2549-2577) +- `ParametriGetFilt` (Lines 2585-2614) +- `POdlGetByKey` (Lines 2658-2697) +- `POdlGetByOdl` (Lines 2705-2744) +- `POdlListByKitParent` (Lines 2770-2800) +- `POdlListGetFilt` (Lines 2812-2841) +- `TksScore` (Lines 3343-3372) +- `VocabolarioGetAll` (Lines 3445-3477) +- `WipKitFilt` (Lines 3543-3570) + +### 2. Implementation Steps +For each candidate method: +1. **Convert to Async**: If the method is synchronous (e.g., `AnagEventiGeneral`), convert it to `Task` to match the `GetOrFetchAsync` signature. +2. **Map Keys**: Ensure the `cacheKey` passed to `GetOrFetchAsync` is identical to the old Redis key to prevent cache fragmentation. +3. **Set Expiration**: Use appropriate `TimeSpan` (e.g., `redisLongTimeCache` or `redisShortTimeCache` converted to `TimeSpan`). +4. **Cleanup**: Remove manual `JsonConvert` logic and `redisDb.StringGet/Set` calls. +5. **Verify Tracing**: Ensure `ActivitySource` and `LogTrace` are preserved or integrated within the `GetOrFetchAsync` wrapper. + +### 3. Validation +- Ensure all refactored methods are still called correctly by consumers. +- Verify that `GetOrFetchAsync` correctly hits Memory first, then Redis, then DB. +- Confirm that `LogTrace` still reports the correct source (MEMORY, REDIS, or DB).