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

280 lines
8.3 KiB
Markdown

# 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