3.3 KiB
Refactor: VocabolarioService Traduci
Stato Attuale (pre-refactor)
VocabolarioServiceè Singleton (DataServiceCollectionExtensions:118)Traduci(lingua, lemma)è un mockup che ritorna"lingua_lemma"(linea 107)- Codice di fallback commented-out (linee 105-125): tentava carico lazy su Dictionary statica
EnsureInitializedAsync()esisteva ma non veniva mai chiamatoDictVocaberaprotected static Dictionary<string,string>— non thread-safe- CRUD (Upsert, UpsertMany, Delete, Clone) invalidano la cache Redis con
ClearCacheAsync(...) ListLingueAsync,GetAllAsync,GetByLangusano tuttiGetOrSetCacheAsyncsu Redis
Progettazione
Obiettivo: Traduci() sincrona, rapidissima (dizionario in-memory), con invalidazione su cambio vocabolario.
Scelte:
-
Niente static — rimuovo
protected static DictVocab. Il servizio è già Singleton, quindi un'istanza per processo è sufficiente. Elimino rischi di leak in test/iniezione. -
ConcurrentDictionary<string, Dictionary<string, string>> — struttura a due livelli:
Lingua -> Dictionary<Lemma, Traduzione>. Lookup 0 allocations. Blazor Server usa circuiti concorrenti, quindiConcurrentDictionaryè sicuro per consultazione. -
Lazy loading con single-check + SemaphoreSlim — primo caricamento lazy sulla prima chiamata a
Traduci(). Dopo, il dizionario è sempre popolato. -
Invalidazione sincrona dopo CRUD — ogni metodo CRUD che modifica i dati richiama
_syncDictFromRepo()dopoClearCacheAsync(). Questo mantiene coerenza tra Redis-cache e dizionario in-memory. -
Fallback silenzioso — se una lingua o lemma non esiste, ritorno il lemma stesso (standard i18n: "untranslated → use key"). Se il load dal DB fallisce, resto con lo stato precedente — non interrompo il flusso.
Modifiche ESEGUITE
VocabolarioService.cs
Rimosso:
protected static Dictionary<string, string> DictVocab— campo statico non thread-safe e mai utilizzatoEnsureInitializedAsync()— metodo morto, mai chiamato
Aggiunto:
_translations: ConcurrentDictionary<string, Dictionary<string, string>>— dizionario a doppia chiave (Lingua → Lemma → Traduzione), case-insensitive su entrambi i livelli_initLock: SemaphoreSlim— lock per caricamento lazy thread-safe_initialized: bool— flag single-check_syncDictFromRepo()— metodo private sincrono post-CRUD, ricariche il dizionario completo dal repo- Cambiato il pattern: ogni CRUD (CloneAsync, DeleteAsync, UpsertAsync, UpsertManyAsync) ora chiama
_syncDictFromRepo()dopoClearCacheAsync() - Implementato
Traduci()— prima chiamata aTraduci()→EnsureDictLoaded()che fa lazy-load dal repo; dopo quella.lookup diretta su_translations[lingua][lemma]; fallback →lemma - Nuovo
LoadDictFromRepoAsync()— query_repo.GetAllAsync(), costruisce il ConcurrentDictionary a due livelli
IVocabolarioService.cs
Nessuna modifica — mantenuta la stessa interfaccia. Traduci rimane sync con stessa signature.
Altri file
Nessuna modifica necessitata:
- Repository: nessun cambiamento
- DI Registration
DataServiceCollectionExtensions: nessun cambiamento - DI Registration UI
Program.cs: nessun cambiamento NavMenu.razor/.razor.cs: nessun cambiamento, usaTraduci()come primaVocabulary.razor: nessun cambiamento