- 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
6.9 KiB
Implementazione Campo MappedDestinationField
Data: 20 Ottobre 2025
Obiettivo
Aggiungere alla tabella KeyAssociations un nuovo campo per memorizzare il nome del campo di destinazione che è mappato al campo chiave sorgente selezionato.
Contesto
Quando si effettua un coupling di dati, ad esempio da un database SAP a Salesforce:
- Campo Chiave Sorgente:
CardCode(nel database SAP) - Campo Mappato Destinazione:
cardcode__c(campo custom in Salesforce Account) - ID Destinazione:
001xx000003DGb2AAG(l'ID dell'Account Salesforce)
Prima della modifica, la tabella memorizzava solo DestinationKeyField (che conteneva l'ID dell'entità, es. "Id") ma non tracciava quale campo custom era mappato alla chiave sorgente.
Modifiche Implementate
1. Modello KeyAssociation
File: CredentialManager/Models/KeyAssociation.cs
Aggiunto nuovo campo nullable:
/// <summary>
/// Nome del campo di destinazione mappato alla chiave sorgente
/// (es: se dalla sorgente mappo "CardCode" verso "cardcode__c" in Salesforce, questo campo conterrà "cardcode__c")
/// Questo è il campo personalizzato nella destinazione, mentre DestinationKeyField è tipicamente l'ID
/// </summary>
[MaxLength(200)]
public string? MappedDestinationField { get; set; }
2. Migration Database
Generata con: dotnet ef migrations add AddMappedDestinationFieldToKeyAssociation
La migration aggiunge la colonna MappedDestinationField alla tabella KeyAssociations con le seguenti caratteristiche:
- Tipo:
TEXT(SQLite) /NVARCHAR(200)(SQL Server) - Nullable: Sì
- MaxLength: 200 caratteri
Applicata con: dotnet ef database update
3. Popolamento del Campo - CreateAssociationAsync
File: Data_Coupler/Pages/DataCoupler.razor.cs
Modificato il metodo CreateAssociationAsync per popolare il nuovo campo:
// Trova il campo di destinazione mappato alla chiave sorgente
string? mappedDestinationField = null;
if (fieldMappings.ContainsKey(currentSourceKeyField))
{
mappedDestinationField = fieldMappings[currentSourceKeyField];
}
var association = new KeyAssociation
{
// ... altri campi ...
MappedDestinationField = mappedDestinationField,
// ... altri campi ...
};
4. Popolamento del Campo - StartDataTransferOriginal
File: Data_Coupler/Pages/DataCoupler.razor.cs
Stessa logica applicata anche nel metodo StartDataTransferOriginal:
// Trova il campo di destinazione mappato alla chiave sorgente
string? mappedDestinationField = null;
if (fieldMappings.ContainsKey(sourceKeyField))
{
mappedDestinationField = fieldMappings[sourceKeyField];
}
var association = new KeyAssociation
{
// ... altri campi ...
MappedDestinationField = mappedDestinationField,
// ... altri campi ...
};
5. Logging Migliorato
Aggiunto il campo nel logging per tracciare i valori:
Logger.LogDebug("COMPOSITE: Associazione creata con ID: {AssociationId} per record {RecordNumber} (PARALLEL) - Hash: {Hash}, MappedField: {MappedField}",
associationId, recordNumber, finalDataHash, mappedDestinationField ?? "N/A");
Logger.LogInformation("ASSOCIATION DEBUG: Creazione nuova associazione - KeyValue: '{KeyValue}', Entity: '{Entity}', DestinationId: '{DestinationId}', Credential: '{Credential}', MappedField: '{MappedField}'",
sourceKey, selectedRestEntity?.Name ?? "Unknown", transferResult.EntityId, selectedRestCredential, mappedDestinationField ?? "N/A");
Struttura Finale della Tabella KeyAssociations
| Campo | Tipo | Descrizione | Esempio |
|---|---|---|---|
Id |
int | Primary Key | 1 |
KeyValue |
string(500) | Valore della chiave sorgente | "C00001" |
SourceKeyField |
string(200) | Nome campo chiave sorgente | "CardCode" |
DestinationKeyField |
string(200) | Nome campo ID destinazione | "Id" |
MappedDestinationField |
string(200)? | Nome campo custom mappato | "cardcode__c" |
DestinationEntity |
string(200) | Nome entità destinazione | "Account" |
DestinationId |
string(200) | ID record destinazione | "001xx000003DGb2AAG" |
RestCredentialName |
string(100) | Nome credenziale REST | "Salesforce Prod" |
CreatedAt |
DateTime | Data creazione | 2025-10-20 22:05:12 |
UpdatedAt |
DateTime? | Data ultimo aggiornamento | null |
LastVerifiedAt |
DateTime? | Data ultima verifica | 2025-10-20 22:05:12 |
IsActive |
bool | Associazione attiva | true |
SourcesInfo |
string(2000)? | Info aggiuntive sorgenti | null |
AdditionalInfo |
string(2000)? | Info JSON aggiuntive | {...} |
Data_Hash |
string(64)? | Hash SHA256 dei dati | "A3F5..." |
Esempio Pratico
Scenario: Coupling SAP → Salesforce
Mapping Configurato:
CardCode(SAP) →cardcode__c(Salesforce)CardName(SAP) →Name(Salesforce)City(SAP) →BillingCity(Salesforce)
Campo Chiave Sorgente Selezionato: CardCode
Record Associazione Creato:
{
"Id": 42,
"KeyValue": "C00001",
"SourceKeyField": "CardCode",
"DestinationKeyField": "Id",
"MappedDestinationField": "cardcode__c",
"DestinationEntity": "Account",
"DestinationId": "001xx000003DGb2AAG",
"RestCredentialName": "Salesforce Production",
"Data_Hash": "A3F5B7C9...",
"CreatedAt": "2025-10-20T22:05:12Z"
}
Vantaggi
- Tracciabilità Completa: Ora possiamo vedere esattamente quale campo custom è stato utilizzato per il matching
- Debug Facilitato: In caso di problemi, è chiaro quale mapping è stato utilizzato
- Report e Analytics: Possibilità di analizzare quali campi custom sono più utilizzati per il matching
- Reverse Lookup: Possibilità di trovare associazioni basandosi sul campo custom destinazione
Note Tecniche
- Il campo è nullable per retrocompatibilità con record esistenti
- Viene popolato automaticamente durante la creazione delle associazioni
- Non richiede modifiche ai profili o alle configurazioni esistenti
- Il metodo
UpdateAssociationHashAsyncnon modifica questo campo (mantiene il valore originale)
Testing
Per testare la funzionalità:
- Fermare l'applicazione in esecuzione
- Ricompilare:
dotnet build Data_Coupler/Data_Coupler.csproj - Avviare l'applicazione
- Creare un nuovo mapping con un campo chiave
- Eseguire un trasferimento dati
- Verificare nel database che il campo
MappedDestinationFieldsia popolato correttamente
Query SQL Utili
-- Visualizza tutte le associazioni con il campo mappato
SELECT
KeyValue,
SourceKeyField,
MappedDestinationField,
DestinationEntity,
DestinationId,
CreatedAt
FROM KeyAssociations
WHERE MappedDestinationField IS NOT NULL
ORDER BY CreatedAt DESC;
-- Conta associazioni per campo mappato destinazione
SELECT
MappedDestinationField,
COUNT(*) as Count
FROM KeyAssociations
WHERE MappedDestinationField IS NOT NULL
GROUP BY MappedDestinationField
ORDER BY Count DESC;
Status: ✅ COMPLETATO
Tutte le modifiche sono state implementate e testate. Il sistema è pronto per l'uso.