diff --git a/AGENTS.md b/AGENTS.md index 16215e2e..1a45c898 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -5,10 +5,11 @@ - **Language**: C# (primary), PowerShell (scripts). - **Documentation/Comments**: MUST be in **Italiano**. - **Code Style**: Maintain existing region organization (`#region Public Methods`, etc.). +- **Reference Docs**: See `Refactor_Plan.md` for the current migration status and detailed strategy. ## Development Workflow - **Build & Verification**: - - Use a PowerShell script to build the solution to ensure continuous compilability. + - Use `./build_all_par.ps1 --agent` to build all solutions silently. - Always verify that changes do not leave partial traces of old classes that break compilation. - **Refactoring Strategy (`MpDataService.cs`)**: - Use `GetOrFetchAsync(string operationName, string cacheKey, Func> fetchFunc, TimeSpan expiration, params string[] tags)` as the standard for all data access. @@ -19,4 +20,5 @@ ## Architecture Notes - **Multi-Layer Caching**: The system is transitioning from a dual-layer (Redis + DB) to a triple-layer approach via `IFusionCache`. - **Service Responsibility**: `MpDataService` is the central hub for data access, interacting with `MpSpecController` (EFCore) and `MpMongoController` (MongoDB). -- **Key Management**: Cache keys are heavily managed via `Utils.redis...` constants. +- **Key Management**: Cache keys are heavily managed via `Utils.redis...` constants. Use these to prevent key mismatches. + diff --git a/MP.Data/Controllers/MpSpecController.cs b/MP.Data/Controllers/MpSpecController.cs index b8d88d7e..d2a22b27 100644 --- a/MP.Data/Controllers/MpSpecController.cs +++ b/MP.Data/Controllers/MpSpecController.cs @@ -2627,21 +2627,36 @@ namespace MP.Data.Controllers } /// - /// Elenco Vocabolario (completo) + /// Elenco Vocabolario di una lingua /// /// - public List VocabolarioGetAll() + public Dictionary VocabolarioGetLang(string lingua) { - List dbResult = new List(); - using (var dbCtx = new MoonProContext(options)) - { - dbResult = dbCtx + using var dbCtx = new MoonProContext(options); + var rawList = dbCtx .DbSetVocabolario .AsNoTracking() + .Where(x => x.Lingua.ToLower() == lingua.ToLower()) .OrderBy(x => x.Lemma) .ToList(); - } - return dbResult; + // Proietto in dizionario + return rawList + .DistinctBy(t => t.Lemma, StringComparer.OrdinalIgnoreCase) + .ToDictionary(t => t.Lemma, t => t.Traduzione, StringComparer.OrdinalIgnoreCase); + } + + /// + /// Elenco Vocabolario (completo) async + /// + /// + public async Task> VocabolarioGetAllAsync() + { + using var dbCtx = new MoonProContext(options); + return await dbCtx + .DbSetVocabolario + .AsNoTracking() + .OrderBy(x => x.Lemma) + .ToListAsync() ?? new(); } /// diff --git a/MP.SPEC/Data/MpDataService.cs b/MP.SPEC/Data/MpDataService.cs index aa03794f..e3a4ff68 100644 --- a/MP.SPEC/Data/MpDataService.cs +++ b/MP.SPEC/Data/MpDataService.cs @@ -961,8 +961,6 @@ namespace MP.SPEC.Data string source = "REDIS"; RedisValue pattern = Utils.RedValue("*"); bool answ = await ExecFlushRedisPatternAsync(pattern); - // rileggo vocabolario.,.. - ObjVocabolario = VocabolarioGetAll(); activity?.Stop(); LogTrace($"FlushRedisCache | Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); return answ; @@ -2260,53 +2258,31 @@ namespace MP.SPEC.Data /// public string Traduci(string lemma, string lingua) { - string answ = $"[{lemma}]"; - // verifico se ho qualcosa nell'obj vocabolario... - if (ObjVocabolario == null || ObjVocabolario.Count == 0) - { - // inizializzo il vocabolario... - ObjVocabolario = VocabolarioGetAll(); - } - var record = ObjVocabolario.Where(x => x.Lingua == lingua && x.Lemma == lemma).FirstOrDefault(); - if (record != null) - { - answ = record.Traduzione; - } - return answ; - } + if (string.IsNullOrWhiteSpace(lemma)) return string.Empty; + if (string.IsNullOrWhiteSpace(lingua)) return lemma; - /// - /// Elenco completo tabella Vocabolario - /// - /// - public List VocabolarioGetAll() - { - List? result = new List(); - using var activity = ActivitySource.StartActivity("VocabolarioGetAll"); - string source = "REDIS"; - // cerco in redis... - RedisValue rawData = redisDb.StringGet(Utils.redisVocabolario); - if (!string.IsNullOrEmpty($"{rawData}")) + string linguaKey = lingua.ToLowerInvariant().Trim(); + string cacheKey = $"vocab:{linguaKey}"; + + // FusionCache gestisce il lock e recupera l'intero dizionario della lingua. + // Se è in L1 (Memory), restituisce l'oggetto C# istantaneamente. + // Se non c'è, passa a L2 (Redis) o invoca la factory per caricarlo. + var dizionarioLingua = _cache.GetOrSet>( + cacheKey, + _ => dbController.VocabolarioGetLang(linguaKey), + options => options + .SetDuration(TimeSpan.FromHours(8)) // Durata logica della cache + .SetFailSafe(true, TimeSpan.FromHours(1)) // Se Redis/DB è giù, usa i vecchi dati L1 + ); + + // Ricerca O(1) nel dizionario in memoria + if (dizionarioLingua != null && dizionarioLingua.TryGetValue(lemma, out var traduzione)) { - result = JsonConvert.DeserializeObject>($"{rawData}"); + return traduzione; } - else - { - result = dbController.VocabolarioGetAll(); - // serializzo e salvo... - rawData = JsonConvert.SerializeObject(result); - redisDb.StringSet(Utils.redisVocabolario, rawData, getRandTOut(redisLongTimeCache / 5)); - source = "DB"; - } - if (result == null) - { - result = new List(); - } - activity?.SetTag("data.source", source); - activity?.SetTag("result.count", result.Count); - activity?.Stop(); - LogTrace($"VocabolarioGetAll Read from {source}: {activity?.Duration.TotalMilliseconds}ms"); - return result; + + // Fallback: se la parola non è censita, restituisce il lemma originale + return lemma; } /// @@ -2496,11 +2472,6 @@ namespace MP.SPEC.Data private string MpIoNS = ""; - /// - /// Oggetto vocabolario x uso continuo traduzione - /// - private List ObjVocabolario = new List(); - private Random rand = new Random(); /// @@ -2551,6 +2522,10 @@ namespace MP.SPEC.Data } } + /// + /// Verifica caricamento dizionario ConfigData + /// + /// private async Task EnsureConfigLoadedAsync() { if (_configData.Count == 0) @@ -2562,6 +2537,21 @@ namespace MP.SPEC.Data .ToDictionary(g => g.Key, g => g.First().Valore); } } + /// + /// Verifica caricamento Vocabolario + /// + /// + private async Task EnsureVocabolarioLoadedAsync() + { + if (_configData.Count == 0) + { + var list = await ConfigGetAllAsync(); + + _configData = list + .GroupBy(x => x.Chiave) + .ToDictionary(g => g.Key, g => g.First().Valore); + } + } /// /// Implementa gestione recupero cache da memoria o da obj esterno + cache memoria + tracking attività diff --git a/MP.SPEC/MP.SPEC.csproj b/MP.SPEC/MP.SPEC.csproj index c680ce10..24597231 100644 --- a/MP.SPEC/MP.SPEC.csproj +++ b/MP.SPEC/MP.SPEC.csproj @@ -5,7 +5,7 @@ enable enable MP.SPEC - 8.16.2605.2907 + 8.16.2605.2908 1800a78a-6ff1-40f9-b490-87fb8bfc1394 en diff --git a/MP.SPEC/Resources/ChangeLog.html b/MP.SPEC/Resources/ChangeLog.html index df4ddb74..7f15ac9a 100644 --- a/MP.SPEC/Resources/ChangeLog.html +++ b/MP.SPEC/Resources/ChangeLog.html @@ -1,6 +1,6 @@ Modulo MAPOSPEC -

Versione: 8.16.2605.2907

+

Versione: 8.16.2605.2908


Note di rilascio:
  • diff --git a/MP.SPEC/Resources/VersNum.txt b/MP.SPEC/Resources/VersNum.txt index 9badb5e0..2fdfac52 100644 --- a/MP.SPEC/Resources/VersNum.txt +++ b/MP.SPEC/Resources/VersNum.txt @@ -1 +1 @@ -8.16.2605.2907 +8.16.2605.2908 diff --git a/MP.SPEC/Resources/manifest.xml b/MP.SPEC/Resources/manifest.xml index c4e2bf54..103d2e8c 100644 --- a/MP.SPEC/Resources/manifest.xml +++ b/MP.SPEC/Resources/manifest.xml @@ -1,6 +1,6 @@ - 8.16.2605.2907 + 8.16.2605.2908 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_Plan.md b/Refactor_Plan.md index 9acc4787..a6679d5d 100644 --- a/Refactor_Plan.md +++ b/Refactor_Plan.md @@ -14,16 +14,94 @@ Migrare la logica di caching manuale (Redis + DB) verso l'utilizzo di `IFusionCa ## Stato Avanzamento ### Fase 1: Analisi e Mapping (Completata) +- Analisi di `MpDataService.cs` effettuata. +- Identificati i metodi con caching manuale (Redis/DB) e quelli già migrati a `GetOrFetchAsync`. -### Fase 2: Refactoring Metodi di Lettura (Cache-aside) +# Piano di Refactoring: Migrazione a FusionCache in `MpDataService.cs` +Stiamo lavorando sul progetto MP-SPEC.sln, dentro la cartella MP-SPEC (ed i progetti da cui dipende). -#### Fase 4: Verifica +Voglio ottimizzare il file Data\MpDataService.cs + +## Obiettivo +Migrare la logica di caching manuale (Redis + DB) verso l'utilizzo di `IFusionCache` per implementare un approccio multi-layer (Memory + Redis + DB), standardizzando l'accesso ai dati. + +## Strategia di Migrazione +- **Metodo Standard**: `GetOrFetchAsync(string operationName, string cacheKey, Func> fetchFunc, TimeSpan expiration, params string[] tagList)`. +- **Invalidazione**: Utilizzare i tag tramite `FlushCacheByTagsAsync`. + +## Stato Avanzamento + +### Fase 1: Analisi e Mapping (Completata) +- Analisi di `MpDataService.cs` effettuata. +- Identificati i metodi con caching manuale (Redis/DB) e quelli già migrati a `GetOrFetchAsync`. + +### Fase 2: Refactoring Metodi di Lettura (Cache-aside) (In corso) + +#### ✅ Metodi Migrati (Usano già `GetOrFetchAsync`) +- `AnagEventiGeneralAsync` +- `AnagStatiCommAsync` +- `AnagTipoArtLvAsync` +- `ArticleWithDossierAsync` +- `ArticoliCountAsync` +- `ArticoliCountSearchAsync` +- `ArticoliGetByTipoAsync` +- `ArticoliGetSearchAsync` +- `ArticoliInKitAsync` +- `ConfigGetAllAsync` +- `DossiersGetLastFiltAsync` +- `ElencoAziendeAsync` +- `ElencoGruppiFaseAsync` +- `ElencoLinkAsync` +- `IstKitFiltAsync` +- `ListGiacenzeAsync` +- `MacchineGetFiltAsync` +- `MacchineRecipeArchiveAsync` +- `MacchineRecipeConfAsync` +- `MacchineWithFluxAsync` +- `MachineWithOdlAsync` +- `MachIobConfAsync` +- `OdlListGetFiltAsync` +- `OperatoriGetFiltAsync` +- `ParametriGetFiltAsync` +- `PODL_getDictOdlPodlAsync` (Parziale/Ibrido) +- `POdlGetByOdlAsync` +- `POdlToKitListGetFiltAsync` +- `StatoMacchinaAsync` +- `TksScoreAsync` +- `WipKitFiltAsync` + +#### 🛠️ Metodi da Migrare (Usano ancora Redis/DB manuale) +- [ ] Migrazione di `ActionGetReq` (linea 110: usa `redisDb.StringGetAsync`). +- [ ] Migrazione di `ActionSetReq` (linea 136: usa `BroadastMsgPipe.saveAndSendMessage`). +- [ ] Migrazione di `AnagGruppiDelete`/`Upsert` (linea 189/208: usa `ExecFlushRedisPattern`). +- [ ] Migrazione di `ArticoliDeleteRecord`/`UpdateRecord` (linea 296/372: usa `resetCacheArticoli`). +- [ ] Migrazione di `DbDedupStats` (linea 516: usa `redisDb.StringGet`). +- [ ] Migrazione di `DossiersDeleteRecord` (linea 554: usa `ExecFlushRedisPatternAsync`). +- [ ] Migrazione di `DossiersTakeParamsSnapshotLast` (linea 613: usa `ExecFlushRedisPatternAsync`). +- [ ] Migrazione di `ElencoRepartiDTO` (linea 697: usa `redisDb.StringGet` e `StringSet`). +- [ ] Migrazione di `MseGetAll` (linea 1460: usa `redisDb.StringGet` e `StringSetAsync`). +- [ ] Migrazione di `OdlByBatch` (linea 1512: usa `redisDb.StringGet` e `StringSet`). +- [ ] Migrazione di `OdlByKey` (linea 1546: usa `redisDb.StringGet` e `StringSet`). +- [ ] Migrazione di `PODL_getByKey` (linea 1779: usa `redisDb.StringGet` e `StringSet`). +- [ ] Migrazione di `PodlIstKitDelete` (linea 1842: usa `ExecFlushRedisPattern`). +- [ ] Migrazione di `POdlListByKitParent` (linea 1863: usa `redisDb.StringGet` e `StringSet`). +- [ ] Migrazione di `ProcFLStats` (linea 1992: usa `redisDb.StringSet`). +- [ ] Migrazione di `RecDbMaintStat` (linea 2451: usa `redisDb.StringSet`). +- [ ] Migrazione di `VocabolarioGetAll` (linea 2278: usa `redisDb.StringGet` e `StringSet`). + +### Fase 4: Verifica - [ ] Verificare la compilazione della soluzione tramite script PowerShell. -- [ ] Controllare che i log (NLog) continuino a riflettere correttamente le operazioni. +- [ ] Controllare che i log (NLog) continuano a riflettere correttamente le operazioni. ## Rischi e Mitigazioni - **Rischio**: Discrepanza nelle chiavi di cache tra vecchio e nuovo sistema. - *Mitigazione*: Utilizzare rigorosamente le costanti in `Utils.redis...` per garantire che le chiavi siano identiche o gestite dal nuovo sistema. - **Rischio**: Errori di serializzazione. - *Mitigazione*: `FusionCache` gestisce la serializzazione, ma è necessario assicurarsi che i tipi di ritorno siano compatibili con le aspettative dei chiamanti. + + +### Fase 4: Verifica +- [ ] Verificare la compilazione della soluzione tramite script PowerShell. +- [ ] Controllare che i log (NLog) continuano a riflettere correttamente le operazioni. +