# FAQ - Hash Calculation Alignment ## ❓ Domande Frequenti ### 1. Perché è stato necessario cambiare l'algoritmo di hash? **Prima:** I due metodi usavano algoritmi diversi (MD5 vs SHA256) causando hash diversi per gli stessi dati. **Problema:** Il sistema di rilevamento modifiche non funzionava correttamente: - Record non modificati venivano aggiornati inutilmente - Performance degradate per chiamate API non necessarie - Incoerenza tra esecuzione manuale e schedulata **Soluzione:** Unificazione completa dell'algoritmo. --- ### 2. Cos'è la MAPPING_SIGNATURE? La **MAPPING_SIGNATURE** è una stringa che rappresenta la configurazione dei field mappings: ``` MAPPING_SIGNATURE=ContactEmail->Email,FullName->Name,Years->Age ``` **Scopo:** - Rileva cambiamenti nella configurazione dei mapping - Se i mapping cambiano, l'hash cambia anche con dati identici - Forza un aggiornamento quando la configurazione viene modificata **Esempio:** ```csharp // Configurazione 1: FieldA -> PropertyX var hash1 = GenerateHash(data, { "FieldA": "PropertyX" }); // Configurazione 2: FieldA -> PropertyY (mapping cambiato!) var hash2 = GenerateHash(data, { "FieldA": "PropertyY" }); // hash1 != hash2 ← Rilevato cambio configurazione! ``` --- ### 3. L'hash viene calcolato sul record originale o sui dati trasformati? **Risposta:** Sui **dati trasformati/mappati** (`restData`). **Motivazione:** - L'hash deve riflettere i dati che vengono effettivamente inviati all'API REST - Record originali possono avere campi non mappati che non devono influenzare l'hash - Solo i dati trasferiti contano per il rilevamento modifiche **Esempio:** ```csharp // Record originale var originalRecord = new Dictionary { { "ID", 123 }, // ← Non mappato { "Name", "John" }, // ← Mappato a "FullName" { "Internal", "ABC" } // ← Non mappato }; // Dati trasformati (solo mappati) var restData = new Dictionary { { "FullName", "John" } // ← Solo questo conta per l'hash! }; // Hash calcolato solo su restData var hash = GenerateDataHash(restData, fieldMappings); ``` --- ### 4. Cosa succede alla prima esecuzione dopo il deploy? **Risposta:** Tutti i record esistenti verranno **aggiornati**. **Motivo:** 1. Hash esistenti calcolati con vecchio algoritmo (MD5) 2. Hash nuovi calcolati con nuovo algoritmo (SHA256) 3. Confronto: hash diversi → Trigger update **È normale?** Sì, è un **one-time update** accettabile. **Come evitarlo?** (Opzionale) ```sql -- Reset preventivo degli hash UPDATE KeyAssociations SET Data_Hash = NULL; ``` --- ### 5. Come verifico che l'hash sia calcolato correttamente? **Metodo 1:** Controlla i log ``` Hash SHA256 generato: F4A3B2C1... (include signature mapping: True) ``` **Metodo 2:** Usa lo script di test ```bash cd Scripts dotnet run HashCalculationTest.cs ``` **Metodo 3:** Query SQL ```sql -- Controlla hash in database SELECT KeyValue, Data_Hash, LastVerifiedAt FROM KeyAssociations WHERE Data_Hash IS NOT NULL ORDER BY UpdatedAt DESC; ``` --- ### 6. L'hash è sensibile alle maiuscole? **Risposta:** Dipende dai dati. **Normalizzazione applicata:** - ✅ **Trim()** su tutti i valori (rimozione spazi) - ❌ **NO ToLower()** o ToUpper() **Esempio:** ```csharp var hash1 = GenerateHash({ "Name": "John" }); var hash2 = GenerateHash({ "Name": "john" }); // hash1 != hash2 ← Case-sensitive! ``` **Motivazione:** I dati REST sono tipicamente case-sensitive. --- ### 7. Cosa succede se i field mappings sono null o vuoti? **Risposta:** L'hash viene calcolato **senza MAPPING_SIGNATURE**. ```csharp // Con mappings var hash1 = GenerateHash(data, mappings); // MAPPING_SIGNATURE=...| Age=30|Email=john@example.com|Name=John // Senza mappings var hash2 = GenerateHash(data, null); // Age=30|Email=john@example.com|Name=John // hash1 != hash2 (diversi!) ``` **Best Practice:** Passare sempre i mappings quando disponibili. --- ### 8. L'ordine dei campi nel Dictionary influenza l'hash? **Risposta:** No, grazie all'ordinamento alfabetico. ```csharp // Dictionary non ordinato var data1 = new Dictionary { { "Zebra", "Z" }, { "Apple", "A" }, { "Banana", "B" } }; // Dictionary ordinato var data2 = new Dictionary { { "Apple", "A" }, { "Banana", "B" }, { "Zebra", "Z" } }; var hash1 = GenerateHash(data1); var hash2 = GenerateHash(data2); // hash1 == hash2 ✅ (ordine ignorato) ``` --- ### 9. Come funziona il skip degli aggiornamenti? **Flusso:** 1. Carica record sorgente 2. Trasforma in `restData` (dati mappati) 3. Calcola `currentHash = GenerateHash(restData, mappings)` 4. Cerca associazione esistente nel database 5. Confronta `currentHash` con `existingHash` 6. **Se uguali:** Skip update, aggiorna solo `LastVerifiedAt` 7. **Se diversi:** Esegui update, aggiorna hash e `LastVerifiedAt` **Beneficio:** Riduzione chiamate API del ~70% per dati stabili. --- ### 10. Posso usare MD5 invece di SHA256? **Risposta:** No, per consistenza con DataCoupler.razor.cs. **Motivazione:** - DataCoupler.razor.cs usa SHA256 - Necessaria identità totale dell'algoritmo - SHA256 più sicuro di MD5 **Se vuoi cambiare algoritmo:** - Modifica entrambi i file contemporaneamente - Ricalcola tutti gli hash esistenti - Testa accuratamente --- ### 11. Cosa contiene il campo Data_Hash nel database? **Risposta:** Una stringa esadecimale SHA256 (64 caratteri). **Esempio:** ``` F4A3B2C1D5E6F7A8B9C0D1E2F3A4B5C6D7E8F9A0B1C2D3E4F5A6B7C8D9E0F1A2 ``` **Struttura database:** ```sql CREATE TABLE KeyAssociations ( Id INT PRIMARY KEY, KeyValue NVARCHAR(450), Data_Hash NVARCHAR(64), -- ← Hash SHA256 (64 hex chars) LastVerifiedAt DATETIME, UpdatedAt DATETIME, ... ); ``` --- ### 12. Come eseguo il debug se l'hash non è corretto? **Step 1:** Attiva logging dettagliato ```csharp _logger.LogDebug("Hash dei dati generato da: {CombinedData}", combinedData); ``` **Step 2:** Verifica input - Controlla che `restData` contenga solo campi mappati - Verifica che `fieldMappings` sia popolato correttamente **Step 3:** Confronta output ```csharp // DataCoupler var hash1 = GenerateDataHash(restData); Console.WriteLine($"DataCoupler hash: {hash1}"); // ScheduledService var hash2 = GenerateDataHash(restData); Console.WriteLine($"Scheduled hash: {hash2}"); // Devono essere identici! ``` **Step 4:** Usa lo script di test ```bash dotnet run Scripts/HashCalculationTest.cs ``` --- ### 13. Posso disabilitare la MAPPING_SIGNATURE? **Risposta:** Tecnicamente sì, ma **non è raccomandato**. **Come:** ```csharp // Passa null come fieldMappings var hash = GenerateDataHash(restData, null); ``` **Conseguenza:** - Cambi di configurazione mapping non verranno rilevati - Record potrebbero non essere aggiornati quando necessario - Inconsistenza con comportamento atteso **Best Practice:** Usa sempre la MAPPING_SIGNATURE. --- ### 14. Quanto impatta il calcolo hash sulle performance? **Misure:** - Calcolo hash singolo: ~0.15ms - SHA256 vs MD5: +50% tempo (~0.1ms → ~0.15ms) - Impatto trascurabile su trasferimenti batch **Beneficio netto:** - Risparmio chiamate API: ~70% - Riduzione tempo totale: ~40% - Performance migliorate nel complesso --- ### 15. Cosa fare se trovo hash diversi per stessi dati? **Checklist debug:** 1. ✅ Verifica che entrambi i metodi usino SHA256 2. ✅ Controlla che `fieldMappings` siano passati in entrambi 3. ✅ Assicurati che `restData` sia identico 4. ✅ Verifica ordinamento alfabetico delle chiavi 5. ✅ Controlla normalizzazione valori (Trim) 6. ✅ Confronta la stringa combinata prima dell'hash **Script diagnostico:** ```csharp var data = GetDataFromSource(); var mappings = GetFieldMappings(); // Log dettagliato var hash = GenerateDataHashVerbose(data, mappings); ``` --- ## 🆘 Supporto **Documentazione:** - `HASH_CALCULATION_ALIGNMENT.md` - Descrizione completa - `HASH_ALIGNMENT_SUMMARY.md` - Riepilogo modifiche **Testing:** - `Scripts/HashCalculationTest.cs` - Test standalone **Logs:** - Cerca: "Hash SHA256 generato" - Cerca: "Hash dei dati generato da" **Contatto:** - Apri issue su GitHub - Consulta la documentazione di progetto