# Fix MappedDestinationField Non Salvato nel Database ## 🐛 Problema Identificato Il campo `MappedDestinationField` veniva popolato correttamente nell'oggetto `KeyAssociation` ma **non veniva scritto nel database**. ### Causa Root Il metodo `SaveAssociationParallelAsync` in `KeyAssociationService.cs` utilizzava query SQL raw che **non includevano** il campo `MappedDestinationField` né nell'UPDATE né nell'INSERT. ## ✅ Correzione Implementata ### File Modificato **`CredentialManager/Services/KeyAssociationService.cs`** #### Metodo: `SaveAssociationParallelAsync` (linea ~159) ### 1. Aggiunta Cattura del Valore ```csharp var mappedDestinationField = association.MappedDestinationField; // AGGIUNTO ``` ### 2. Query UPDATE Corretta **Prima:** ```sql UPDATE KeyAssociations SET DestinationId = {0}, SourceKeyField = {1}, DestinationKeyField = {2}, UpdatedAt = {3}, LastVerifiedAt = {4}, AdditionalInfo = {5}, Data_Hash = {6} WHERE KeyValue = {7} AND DestinationEntity = {8} AND RestCredentialName = {9} AND IsActive = 1 ``` **Dopo:** ```sql UPDATE KeyAssociations SET DestinationId = {0}, SourceKeyField = {1}, DestinationKeyField = {2}, UpdatedAt = {3}, LastVerifiedAt = {4}, AdditionalInfo = {5}, Data_Hash = {6}, MappedDestinationField = {7} ← AGGIUNTO WHERE KeyValue = {8} ← Indici aggiornati AND DestinationEntity = {9} ← Indici aggiornati AND RestCredentialName = {10} ← Indici aggiornati AND IsActive = 1 ``` **Parametri aggiornati:** ```csharp destinationId, sourceKeyField, destinationKeyField, currentTime, currentTime, additionalInfo ?? (object)DBNull.Value, dataHash ?? (object)DBNull.Value, mappedDestinationField ?? (object)DBNull.Value, // AGGIUNTO keyValue, destinationEntity, restCredentialName ``` ### 3. INSERT Corretto (Entity Framework) **Prima:** ```csharp var newAssociation = new KeyAssociation { KeyValue = keyValue, SourceKeyField = sourceKeyField, DestinationKeyField = destinationKeyField, // MappedDestinationField mancante! DestinationEntity = destinationEntity, DestinationId = destinationId, // ... }; ``` **Dopo:** ```csharp var newAssociation = new KeyAssociation { KeyValue = keyValue, SourceKeyField = sourceKeyField, DestinationKeyField = destinationKeyField, MappedDestinationField = mappedDestinationField, // AGGIUNTO DestinationEntity = destinationEntity, DestinationId = destinationId, // ... }; ``` ### 4. Logging Migliorato **Prima:** ```csharp _logger.LogDebug("PARALLEL: Tentativo salvataggio associazione - KeyValue: '{KeyValue}', DestinationEntity: '{DestinationEntity}', DestinationId: '{DestinationId}', RestCredentialName: '{RestCredentialName}'", keyValue, destinationEntity, destinationId, restCredentialName); ``` **Dopo:** ```csharp _logger.LogDebug("PARALLEL: Tentativo salvataggio associazione - KeyValue: '{KeyValue}', DestinationEntity: '{DestinationEntity}', DestinationId: '{DestinationId}', RestCredentialName: '{RestCredentialName}', MappedField: '{MappedField}'", keyValue, destinationEntity, destinationId, restCredentialName, mappedDestinationField ?? "NULL"); // E per il log di creazione: _logger.LogDebug("PARALLEL: Nuova associazione creata: KeyValue={KeyValue} -> {DestinationEntity}/{DestinationId}, MappedField={MappedField}", keyValue, destinationEntity, destinationId, mappedDestinationField ?? "NULL"); ``` ## 🧪 Testing ### Procedura di Test 1. **Fermare l'applicazione** attualmente in esecuzione 2. **Ricompilare il progetto**: ```powershell dotnet build Data_Coupler.sln ``` 3. **Riavviare l'applicazione** 4. **Eseguire un nuovo trasferimento dati**: - Configurare un mapping (es: Email → EmailAddress) - Selezionare "Email" come campo chiave - Eseguire il trasferimento 5. **Verificare nei log**: ``` PARALLEL: Tentativo salvataggio associazione - ... MappedField: 'EmailAddress' PARALLEL: Nuova associazione creata: ... MappedField: EmailAddress ``` 6. **Verificare nel database**: ```sql SELECT Id, KeyValue, SourceKeyField, MappedDestinationField, -- Questo campo ora deve essere popolato! DestinationKeyField, DestinationId FROM KeyAssociations ORDER BY CreatedAt DESC LIMIT 10; ``` ### Risultato Atteso **Prima del Fix:** | Id | KeyValue | SourceKeyField | MappedDestinationField | DestinationKeyField | DestinationId | |----|----------|----------------|------------------------|---------------------|---------------| | 1 | C00001 | CardCode | NULL ❌ | Id | ABC123 | | 2 | C00026 | CardCode | NULL ❌ | Id | DEF456 | **Dopo il Fix:** | Id | KeyValue | SourceKeyField | MappedDestinationField | DestinationKeyField | DestinationId | |----|----------|----------------|------------------------|---------------------|---------------| | 1 | C00001 | CardCode | **EmailAddress** ✅ | Id | ABC123 | | 2 | C00026 | CardCode | **EmailAddress** ✅ | Id | DEF456 | ## 📊 Impatto della Correzione ### Componenti Affetti - ✅ **Creazione nuove associazioni**: Ora salva correttamente il campo - ✅ **Aggiornamento associazioni esistenti**: Ora aggiorna correttamente il campo - ✅ **Logging**: Ora mostra il valore nel log per debug - ✅ **UI KeyAssociations**: Ora mostrerà il valore invece di "N/A" ### Retrocompatibilità ✅ **Completamente compatibile**: - Le associazioni esistenti (con campo NULL) continueranno a funzionare - Le nuove associazioni avranno il campo popolato - Nessuna migration aggiuntiva richiesta (il campo è già nel database) ## 🔍 Spiegazione Tecnica ### Perché il Campo Non Veniva Salvato? Il metodo `SaveAssociationParallelAsync` usa un pattern di **upsert ottimizzato** per gestire race conditions in operazioni parallele: 1. **Primo tentativo**: UPDATE via SQL raw 2. **Se fallisce**: INSERT via Entity Framework Il problema era che: - ❌ La query SQL raw dell'UPDATE **non includeva** `MappedDestinationField` - ❌ L'oggetto Entity Framework dell'INSERT **non assegnava** `MappedDestinationField` ### Perché Usare SQL Raw? ```csharp // SQL Raw per UPDATE (più performante e thread-safe) await parallelContext.Database.ExecuteSqlRawAsync(@"UPDATE ..."); // Entity Framework per INSERT (più semplice per gestire race conditions) parallelContext.KeyAssociations.Add(newAssociation); await parallelContext.SaveChangesAsync(); ``` **Vantaggi**: - Performance migliori per UPDATE - Gestione automatica race conditions per INSERT - Thread-safe con DbContext separati ## ✅ Checklist Verifica - [x] Campo aggiunto alla query UPDATE SQL - [x] Campo aggiunto all'oggetto INSERT Entity Framework - [x] Parametri query SQL aggiornati con indici corretti - [x] Logging aggiornato per includere MappedDestinationField - [x] Verifica assenza errori di compilazione - [ ] **Fermare applicazione** - [ ] **Ricompilare progetto** - [ ] **Riavviare applicazione** - [ ] **Eseguire test trasferimento** - [ ] **Verificare log contiene "MappedField: 'XXX'"** - [ ] **Verificare database con SELECT** ## 🎯 Prossimi Passi IMMEDIATI 1. ⛔ **FERMARE** l'applicazione in esecuzione 2. 🔨 **Ricompilare**: ```powershell dotnet build Data_Coupler.sln ``` 3. ▶️ **Riavviare** l'applicazione 4. 🧪 **Test completo**: - Crea nuovo mapping con campo chiave - Esegui trasferimento - Verifica log: `"MappedField: 'EmailAddress'"` - Query database per conferma 5. 🗑️ **Opzionale**: Cancella vecchie associazioni di test (con campo NULL) ## 📝 Note Aggiuntive ### Cancellare Associazioni Vecchie (Opzionale) Se vuoi pulire le associazioni di test create prima del fix: ```sql -- Mostra associazioni con campo NULL SELECT * FROM KeyAssociations WHERE MappedDestinationField IS NULL; -- Cancella associazioni di test (ATTENZIONE!) DELETE FROM KeyAssociations WHERE MappedDestinationField IS NULL AND CreatedAt > '2025-10-19'; -- Solo quelle create oggi ``` ### Verifica Migration Database Se il database non ha la colonna, esegui: ```powershell cd CredentialManager dotnet ef database update ``` --- **Data Correzione**: 20 Ottobre 2025 **Versione**: 3.0 - Fix Salvataggio Database **Status**: ✅ Pronto per test - Ricompilazione richiesta