# Correzione Logica MappedDestinationField ## 📋 Problema Identificato Il campo `MappedDestinationField` nella tabella `KeyAssociations` non veniva popolato correttamente perché la logica di ricerca era invertita. ### ❌ Logica Errata Precedente Il codice cercava di trovare il campo **destinazione** mappato al campo chiave **sorgente**: ```csharp // SBAGLIATO: Cercava il valore nel dictionary usando sourceKeyField come chiave if (fieldMappings.ContainsKey(currentSourceKeyField)) { mappedDestinationField = fieldMappings[currentSourceKeyField]; } ``` **Problema**: Questo non aveva senso perché: - Il campo chiave sorgente (`SourceKeyField`) è già memorizzato separatamente - Non serviva sapere a cosa era mappato il campo chiave sorgente - Il mapping cambia per ogni trasferimento ## ✅ Logica Corretta Implementata ### Obiettivo del Campo `MappedDestinationField` deve memorizzare il **campo sorgente** che è mappato al campo fisso **"DestinationId"** nella destinazione REST. ### Struttura del Mapping Nel dictionary `fieldMappings`: - **Chiave**: Nome del campo nella sorgente (database, CSV, Excel) - **Valore**: Nome del campo nella destinazione (entità REST API) Esempio: ```csharp fieldMappings = new Dictionary { { "CodiceFiscale", "DestinationId" }, // Campo da memorizzare { "Nome", "FirstName" }, { "Cognome", "LastName" } } ``` In questo caso, `MappedDestinationField` deve contenere **"CodiceFiscale"**. ### Nuova Implementazione ```csharp // CORRETTO: Cerca quale campo sorgente è mappato a "DestinationId" var mappingToDestinationId = fieldMappings.FirstOrDefault(m => m.Value == "DestinationId"); if (!string.IsNullOrEmpty(mappingToDestinationId.Key)) { mappedSourceField = mappingToDestinationId.Key; Logger.LogDebug("Campo sorgente '{SourceField}' è mappato a 'DestinationId'", mappedSourceField); } ``` ## 🔧 File Modificati ### 1. `DataCoupler.razor.cs` #### Metodo `CreateAssociationAsync` (linea ~2890) **Prima:** ```csharp // Trova il campo di destinazione mappato alla chiave sorgente string? mappedDestinationField = null; if (fieldMappings.ContainsKey(currentSourceKeyField)) { mappedDestinationField = fieldMappings[currentSourceKeyField]; } ``` **Dopo:** ```csharp // Trova il campo sorgente che è mappato a "DestinationId" string? mappedSourceField = null; var mappingToDestinationId = fieldMappings.FirstOrDefault(m => m.Value == "DestinationId"); if (!string.IsNullOrEmpty(mappingToDestinationId.Key)) { mappedSourceField = mappingToDestinationId.Key; } ``` #### Metodo `StartDataTransferOriginal` (linea ~1400) Stessa correzione applicata anche al metodo di trasferimento originale (non composite). ### 2. `ScheduledProfileExecutionService.cs` #### Metodo `CreateAssociationAsync` (linea ~975) **Prima:** ```csharp int mappingCount = 0; // ... calcolo mappingCount ... var association = new KeyAssociation { // ... altri campi ... // MappedDestinationField non veniva popolato }; ``` **Dopo:** ```csharp int mappingCount = 0; string? mappedSourceField = null; if (!string.IsNullOrEmpty(profile.FieldMappingJson)) { var mappings = ParseFieldMappings(profile.FieldMappingJson); // Cerca il campo sorgente mappato a "DestinationId" var mappingToDestinationId = mappings.FirstOrDefault(m => m.Value == "DestinationId"); if (!string.IsNullOrEmpty(mappingToDestinationId.Key)) { mappedSourceField = mappingToDestinationId.Key; } } var association = new KeyAssociation { // ... altri campi ... MappedDestinationField = mappedSourceField, // Campo sorgente mappato a DestinationId }; ``` ## 📊 Logging Diagnostico ### Log Implementati ```csharp // Traccia ricerca del campo Logger.LogDebug("MAPPING DEBUG: Cercando quale campo sorgente è mappato a 'DestinationId'"); // Mostra tutti i mapping disponibili Logger.LogDebug("MAPPING DEBUG: Mappings disponibili: {Mappings}", string.Join(", ", fieldMappings.Select(m => $"{m.Key} -> {m.Value}"))); // Successo Logger.LogDebug("MAPPING DEBUG: Trovato mapping: campo sorgente '{SourceField}' è mappato a 'DestinationId'", mappedSourceField); // Fallimento (warning) Logger.LogWarning("MAPPING DEBUG: Nessun campo sorgente mappato a 'DestinationId'! Il campo non verrà popolato."); // Log creazione associazione Logger.LogDebug("COMPOSITE: Associazione creata con ID: {AssociationId} per record {RecordNumber} - Hash: {Hash}, MappedField: {MappedField}", associationId, recordNumber, finalDataHash, mappedSourceField ?? "N/A"); ``` ## 🧪 Testing ### Pre-Requisiti per il Test 1. **Fermare l'applicazione** in esecuzione 2. **Ricompilare** il progetto: ```bash dotnet build Data_Coupler.sln ``` 3. **Configurare logging Debug** in `appsettings.Development.json`: ```json { "Logging": { "LogLevel": { "Data_Coupler.Pages.DataCoupler": "Debug", "Data_Coupler.Services.ScheduledProfileExecutionService": "Debug" } } } ``` ### Scenario di Test 1. **Configurare mapping** con campo mappato a `DestinationId`: - Esempio: `CodiceFiscale` (sorgente) → `DestinationId` (destinazione) 2. **Selezionare campo chiave** (può essere diverso dal campo mappato a DestinationId): - Esempio: `Email` come SourceKeyField 3. **Eseguire trasferimento dati** 4. **Verificare nei log**: ``` MAPPING DEBUG: Cercando quale campo sorgente è mappato a 'DestinationId' MAPPING DEBUG: Mappings disponibili: CodiceFiscale -> DestinationId, Nome -> FirstName, ... MAPPING DEBUG: Trovato mapping: campo sorgente 'CodiceFiscale' è mappato a 'DestinationId' COMPOSITE: Associazione creata con ID: 123 - MappedField: CodiceFiscale ``` 5. **Verificare nel database**: ```sql SELECT SourceKeyField, -- Campo chiave sorgente (es. "Email") MappedDestinationField, -- Campo sorgente mappato a DestinationId (es. "CodiceFiscale") DestinationKeyField, -- Sempre "Id" o "DestinationId" KeyValue, -- Valore del campo chiave DestinationId -- ID generato dalla REST API FROM KeyAssociations ORDER BY CreatedAt DESC LIMIT 5; ``` ### Risultato Atteso ``` SourceKeyField | MappedDestinationField | DestinationKeyField | KeyValue | DestinationId -----------------------|------------------------|---------------------|----------------------|--------------- Email | CodiceFiscale | Id | user@example.com | ABC123XYZ Email | CodiceFiscale | Id | admin@example.com | DEF456UVW ``` ## 📝 Spiegazione Concettuale ### Differenza tra i Campi | Campo | Descrizione | Esempio | |-------|-------------|---------| | `SourceKeyField` | Campo usato come chiave univoca nella sorgente | "Email" | | `KeyValue` | Valore specifico del campo chiave per questo record | "user@example.com" | | `MappedDestinationField` | Campo sorgente mappato a `DestinationId` nella REST API | "CodiceFiscale" | | `DestinationKeyField` | Campo chiave nella destinazione (sempre "Id" o "DestinationId") | "Id" | | `DestinationId` | ID univoco generato dalla REST API | "ABC123XYZ" | ### Perché è Importante Durante il coupling, il sistema deve: 1. **Identificare record esistenti**: Usa `SourceKeyField` e `KeyValue` 2. **Popolare DestinationId**: Usa il valore del campo sorgente specificato in `MappedDestinationField` 3. **Evitare duplicati**: Verifica se esiste già un'associazione con lo stesso `KeyValue` Esempio pratico: ``` Record CSV: - Email: "user@example.com" <- Usato come SourceKeyField per identificare il record - CodiceFiscale: "RSSMRA80A01H501U" <- Valore da mettere in DestinationId Mapping: - CodiceFiscale → DestinationId <- MappedDestinationField = "CodiceFiscale" - Email → EmailAddress - Nome → FirstName Associazione creata: - SourceKeyField: "Email" - KeyValue: "user@example.com" - MappedDestinationField: "CodiceFiscale" - DestinationId: "RSSMRA80A01H501U" <- Preso dal campo CodiceFiscale del record ``` ## ✅ Checklist Verifica - [x] Correzione logica in `DataCoupler.razor.cs::CreateAssociationAsync` - [x] Correzione logica in `DataCoupler.razor.cs::StartDataTransferOriginal` - [x] Correzione logica in `ScheduledProfileExecutionService.cs::CreateAssociationAsync` - [x] Aggiunta logging diagnostico in tutti i metodi - [x] Verifica assenza errori di compilazione - [ ] Test con trasferimento reale - [ ] Verifica popolamento campo nel database - [ ] Verifica log output corretto ## 🎯 Prossimi Passi 1. **Fermare applicazione in esecuzione** 2. **Ricompilare progetto** 3. **Riavviare applicazione** 4. **Eseguire test trasferimento** 5. **Verificare log output** (cercare "MAPPING DEBUG") 6. **Query database** per confermare campo popolato --- **Data Correzione**: 20 Ottobre 2025 **Versione**: 1.0 - Correzione Logica MappedDestinationField