Files
Data-Coupler/FIX_MAPPED_FIELD_DATABASE_SAVE.md
T
Alessio 5d9b9756cf fix: Correzione salvataggio campo MappedDestinationField in KeyAssociations
- Aggiunto campo MappedDestinationField al modello KeyAssociation per tracciare il campo destinazione mappato alla chiave sorgente
- Creata migration AddMappedDestinationFieldToKeyAssociation per aggiungere la colonna al database
- Implementata logica di popolamento in CreateAssociationAsync e StartDataTransferOriginal per salvare il campo destinazione mappato
- Aggiornato SaveAssociationParallelAsync per includere MappedDestinationField nelle query SQL UPDATE e INSERT
- Corretti indici parametri nella query UPDATE (da {7-9} a {8-10}) per includere il nuovo campo
- Aggiunta visualizzazione campo nell'interfaccia KeyAssociations (tabella, dettagli, export CSV)
- Implementato controllo validazione per impedire trasferimenti se il campo chiave non è mappato
- Aggiunto logging diagnostico dettagliato per debug del mapping dei campi
- Aggiornato ScheduledProfileExecutionService per popolare MappedDestinationField nelle esecuzioni schedulate
- Rimosso file BackgroundServices.cs obsoleto
- Documentazione completa creata (4 markdown files)

Fixes: Campo MappedDestinationField rimaneva NULL perché le query SQL raw non includevano il nuovo campo
2025-10-20 00:42:07 +02:00

8.3 KiB

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

var mappedDestinationField = association.MappedDestinationField; // AGGIUNTO

2. Query UPDATE Corretta

Prima:

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:

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:

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:

var newAssociation = new KeyAssociation
{
    KeyValue = keyValue,
    SourceKeyField = sourceKeyField,
    DestinationKeyField = destinationKeyField,
    // MappedDestinationField mancante!
    DestinationEntity = destinationEntity,
    DestinationId = destinationId,
    // ...
};

Dopo:

var newAssociation = new KeyAssociation
{
    KeyValue = keyValue,
    SourceKeyField = sourceKeyField,
    DestinationKeyField = destinationKeyField,
    MappedDestinationField = mappedDestinationField,  // AGGIUNTO
    DestinationEntity = destinationEntity,
    DestinationId = destinationId,
    // ...
};

4. Logging Migliorato

Prima:

_logger.LogDebug("PARALLEL: Tentativo salvataggio associazione - KeyValue: '{KeyValue}', DestinationEntity: '{DestinationEntity}', DestinationId: '{DestinationId}', RestCredentialName: '{RestCredentialName}'",
    keyValue, destinationEntity, destinationId, restCredentialName);

Dopo:

_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:

    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:

    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?

// 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

  • Campo aggiunto alla query UPDATE SQL
  • Campo aggiunto all'oggetto INSERT Entity Framework
  • Parametri query SQL aggiornati con indici corretti
  • Logging aggiornato per includere MappedDestinationField
  • 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:
    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:

-- 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:

cd CredentialManager
dotnet ef database update

Data Correzione: 20 Ottobre 2025
Versione: 3.0 - Fix Salvataggio Database
Status: Pronto per test - Ricompilazione richiesta