58 lines
3.3 KiB
Markdown
58 lines
3.3 KiB
Markdown
# 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 chiamato
|
|
- `DictVocab` era `protected static Dictionary<string,string>` — non thread-safe
|
|
- CRUD (Upsert, UpsertMany, Delete, Clone) invalidano la cache Redis con `ClearCacheAsync(...)`
|
|
- `ListLingueAsync`, `GetAllAsync`, `GetByLang` usano tutti `GetOrSetCacheAsync` su Redis
|
|
|
|
## Progettazione
|
|
|
|
Obiettivo: `Traduci()` sincrona, rapidissima (dizionario in-memory), con invalidazione su cambio vocabolario.
|
|
|
|
**Scelte:**
|
|
|
|
1. **Niente static** — rimuovo `protected static DictVocab`. Il servizio è già Singleton, quindi un'istanza per processo è sufficiente. Elimino rischi di leak in test/iniezione.
|
|
|
|
2. **ConcurrentDictionary<string, Dictionary<string, string>>** — struttura a due livelli: `Lingua -> Dictionary<Lemma, Traduzione>`. Lookup 0 allocations. Blazor Server usa circuiti concorrenti, quindi `ConcurrentDictionary` è sicuro per consultazione.
|
|
|
|
3. **Lazy loading con single-check + SemaphoreSlim** — primo caricamento lazy sulla prima chiamata a `Traduci()`. Dopo, il dizionario è sempre popolato.
|
|
|
|
4. **Invalidazione sincrona dopo CRUD** — ogni metodo CRUD che modifica i dati richiama `_syncDictFromRepo()` dopo `ClearCacheAsync()`. Questo mantiene coerenza tra Redis-cache e dizionario in-memory.
|
|
|
|
5. **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 utilizzato
|
|
- `EnsureInitializedAsync()` — 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()` dopo `ClearCacheAsync()`
|
|
- **Implementato `Traduci()`** — prima chiamata a `Traduci()` → `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, usa `Traduci()` come prima
|
|
- `Vocabulary.razor`: nessun cambiamento
|