Fix: Risolto double-mapping negli External ID Relationships per Salesforce
- Implementata funzionalità completa External ID Relationships nell'interfaccia di mapping
- Corretto bug double-mapping: i campi sorgente usati per External ID non vengono più inclusi nei mapping normali
- Risolto errore MALFORMED_ID causato dall'invio duplicato di campi come proprietà dirette e nested objects
- Implementata logica corretta per relationship names: oggetti standard usano il nome diretto, custom objects usano suffisso __r
- Aggiunta UI a 3 colonne (Object, External ID Field, Source Field) per configurazione External ID Relationships
- Migrazione database per supporto External ID Relationships nei profili
- Aggiornato ProfileSaver.razor.cs per salvare/caricare External ID Relationships
- Aggiornato ScheduledProfileExecutionService.cs per gestire External ID nelle esecuzioni schedulate
- Formato JSON output corretto: { 'Account': { 'CardCode__c': 'V50000' } }
Documentazione: EXTERNAL_ID_RELATIONSHIPS_IMPLEMENTATION.md
This commit is contained in:
@@ -0,0 +1,345 @@
|
||||
# Fix Connessione SQL Server con Localhost
|
||||
|
||||
**Data**: 15 Febbraio 2026
|
||||
**Versione**: 2.1+
|
||||
|
||||
## 📋 Problema Risolto
|
||||
|
||||
Il sistema non riusciva a connettersi correttamente a SQL Server quando si utilizzava "localhost" come host, specialmente per:
|
||||
- Named Instances (es. `localhost\SQLEXPRESS`)
|
||||
- LocalDB (es. `(localdb)\MSSQLLocalDB`)
|
||||
- Windows Authentication
|
||||
|
||||
## 🔧 Modifiche Implementate
|
||||
|
||||
### 1. ConnectionStringBuilder - Gestione Intelligente del Server
|
||||
|
||||
**File**: `CredentialManager/Models/CredentialModels.cs`
|
||||
|
||||
#### Miglioramenti:
|
||||
|
||||
**a) Named Instances**
|
||||
- Se l'host contiene `\` (backslash), la porta viene omessa automaticamente
|
||||
- Esempi supportati:
|
||||
- `localhost\SQLEXPRESS`
|
||||
- `.\SQLEXPRESS`
|
||||
- `SERVERNAME\INSTANCE`
|
||||
|
||||
**b) LocalDB**
|
||||
- Se l'host inizia con `(localdb)`, la porta viene omessa
|
||||
- Esempi supportati:
|
||||
- `(localdb)\MSSQLLocalDB`
|
||||
- `(localdb)\v11.0`
|
||||
- `(localdb)\ProjectsV13`
|
||||
|
||||
**c) Localhost con Named Pipes**
|
||||
- Per `localhost`, `.` o `127.0.0.1` con porta 1433 (default), la porta viene omessa
|
||||
- Questo permette a SQL Server di usare Named Pipes invece di TCP/IP per connessioni locali più veloci
|
||||
|
||||
**d) Windows Authentication**
|
||||
- Se username è vuoto, `Integrated` o `Windows`, usa Windows Authentication
|
||||
- Non richiede password quando si usa Windows Authentication
|
||||
- Connection string include `Integrated Security=True`
|
||||
|
||||
#### Codice Modificato:
|
||||
|
||||
```csharp
|
||||
private static string BuildSqlServerConnectionString(DatabaseCredential credential)
|
||||
{
|
||||
var builder = new List<string>();
|
||||
|
||||
// Gestione speciale per SQL Server locale e named instances
|
||||
bool hasInstanceName = credential.Host.Contains('\\') ||
|
||||
credential.Host.StartsWith("(localdb)", StringComparison.OrdinalIgnoreCase);
|
||||
|
||||
if (hasInstanceName)
|
||||
{
|
||||
// Per named instances e LocalDB, non includere la porta
|
||||
builder.Add($"Server={credential.Host}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Per localhost con porta default, ometti la porta per usare Named Pipes
|
||||
if ((credential.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase) ||
|
||||
credential.Host == "." ||
|
||||
credential.Host == "127.0.0.1") && credential.Port == 1433)
|
||||
{
|
||||
builder.Add($"Server={credential.Host}");
|
||||
}
|
||||
else
|
||||
{
|
||||
// Per altri casi, usa host,porta
|
||||
builder.Add($"Server={credential.Host},{credential.Port}");
|
||||
}
|
||||
}
|
||||
|
||||
// Windows Authentication vs SQL Authentication
|
||||
if (string.IsNullOrWhiteSpace(credential.Username) ||
|
||||
credential.Username.Equals("Integrated", StringComparison.OrdinalIgnoreCase) ||
|
||||
credential.Username.Equals("Windows", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
builder.Add("Integrated Security=True");
|
||||
}
|
||||
else
|
||||
{
|
||||
builder.Add($"User Id={credential.Username}");
|
||||
builder.Add($"Password={credential.Password}");
|
||||
}
|
||||
|
||||
builder.Add($"Connection Timeout={credential.CommandTimeout}");
|
||||
|
||||
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
||||
builder.Add($"Database={credential.DatabaseName}");
|
||||
|
||||
if (credential.IgnoreSslErrors)
|
||||
builder.Add("TrustServerCertificate=True");
|
||||
|
||||
return string.Join(";", builder);
|
||||
}
|
||||
```
|
||||
|
||||
### 2. UI - Guida Contestuale per SQL Server
|
||||
|
||||
**File**: `Data_Coupler/Pages/CredentialManagement.razor`
|
||||
|
||||
#### Aggiunte:
|
||||
|
||||
**a) Help Text per Host/Server**
|
||||
- Mostra esempi specifici per SQL Server locale:
|
||||
- Named Instance: `localhost\SQLEXPRESS` o `.\SQLEXPRESS`
|
||||
- LocalDB: `(localdb)\MSSQLLocalDB`
|
||||
- Default: `localhost` o `.` (usa porta 1433)
|
||||
|
||||
**b) Nota sulla Porta**
|
||||
- Indica che la porta viene ignorata per named instances e LocalDB
|
||||
|
||||
**c) Guida Windows Authentication**
|
||||
- Nel campo Username: placeholder "o scrivi 'Integrated' per Windows Auth"
|
||||
- Help text: "Per Windows Authentication, scrivi **Integrated** o lascia vuoto"
|
||||
- Nel campo Password: "Non richiesta per Windows Authentication"
|
||||
|
||||
#### Codice Aggiunto:
|
||||
|
||||
```razor
|
||||
@if (currentDatabaseCredential.DatabaseType == DatabaseType.SqlServer)
|
||||
{
|
||||
<div class="form-text">
|
||||
<strong>SQL Server locale:</strong><br/>
|
||||
• Named Instance: <code>localhost\SQLEXPRESS</code> o <code>.\SQLEXPRESS</code><br/>
|
||||
• LocalDB: <code>(localdb)\MSSQLLocalDB</code><br/>
|
||||
• Default: <code>localhost</code> o <code>.</code> (usa porta 1433)
|
||||
</div>
|
||||
}
|
||||
```
|
||||
|
||||
### 3. Validazione Aggiornata
|
||||
|
||||
**File**: `Data_Coupler/Pages/CredentialManagement.razor`
|
||||
|
||||
#### Miglioramenti:
|
||||
|
||||
**a) Validazione Credenziali**
|
||||
- Permette username/password vuoti per SQL Server con Windows Authentication
|
||||
- Riconosce "Integrated" e "Windows" come segnali per Windows Authentication
|
||||
- Validazione più specifica con messaggi di errore appropriati
|
||||
|
||||
#### Codice Modificato:
|
||||
|
||||
```csharp
|
||||
// Per SQL Server, permetti Windows Authentication
|
||||
bool isSqlServerWithWindowsAuth = currentDatabaseCredential.DatabaseType == DatabaseType.SqlServer &&
|
||||
(string.IsNullOrWhiteSpace(currentDatabaseCredential.Username) ||
|
||||
currentDatabaseCredential.Username.Equals("Integrated", StringComparison.OrdinalIgnoreCase) ||
|
||||
currentDatabaseCredential.Username.Equals("Windows", StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
if (!isSqlServerWithWindowsAuth)
|
||||
{
|
||||
// Per database che non usano Windows Authentication, richiedi username e password
|
||||
if (string.IsNullOrEmpty(currentDatabaseCredential.Username) ||
|
||||
string.IsNullOrEmpty(currentDatabaseCredential.Password))
|
||||
{
|
||||
await JSRuntime.InvokeVoidAsync("alert",
|
||||
"Username e Password sono obbligatori. Per SQL Server con Windows Authentication, inserisci 'Integrated' come username.");
|
||||
return;
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 📚 Guida Utilizzo
|
||||
|
||||
### Scenario 1: SQL Server Express Locale
|
||||
|
||||
**Configurazione Credenziale:**
|
||||
- **Host**: `localhost\SQLEXPRESS` o `.\SQLEXPRESS`
|
||||
- **Porta**: 1433 (ignorata)
|
||||
- **Database**: Nome del database (es. `MyDatabase`)
|
||||
- **Username**: `Integrated` o lascia vuoto
|
||||
- **Password**: Lascia vuoto
|
||||
|
||||
**Connection String Generata:**
|
||||
```
|
||||
Server=localhost\SQLEXPRESS;Integrated Security=True;Connection Timeout=30;Database=MyDatabase;TrustServerCertificate=True
|
||||
```
|
||||
|
||||
### Scenario 2: SQL Server LocalDB
|
||||
|
||||
**Configurazione Credenziale:**
|
||||
- **Host**: `(localdb)\MSSQLLocalDB`
|
||||
- **Porta**: 1433 (ignorata)
|
||||
- **Database**: Nome del database (es. `TestDB`)
|
||||
- **Username**: `Integrated` o lascia vuoto
|
||||
- **Password**: Lascia vuoto
|
||||
|
||||
**Connection String Generata:**
|
||||
```
|
||||
Server=(localdb)\MSSQLLocalDB;Integrated Security=True;Connection Timeout=30;Database=TestDB
|
||||
```
|
||||
|
||||
### Scenario 3: SQL Server Locale con SQL Authentication
|
||||
|
||||
**Configurazione Credenziale:**
|
||||
- **Host**: `localhost`
|
||||
- **Porta**: 1433
|
||||
- **Database**: Nome del database (es. `Production`)
|
||||
- **Username**: `sa` (o un altro utente SQL)
|
||||
- **Password**: Password dell'utente
|
||||
|
||||
**Connection String Generata:**
|
||||
```
|
||||
Server=localhost;User Id=sa;Password=***;Connection Timeout=30;Database=Production;TrustServerCertificate=True
|
||||
```
|
||||
|
||||
### Scenario 4: SQL Server Remoto
|
||||
|
||||
**Configurazione Credenziale:**
|
||||
- **Host**: `sql.example.com`
|
||||
- **Porta**: 1433 (o porta custom, es. 14330)
|
||||
- **Database**: Nome del database
|
||||
- **Username**: Utente SQL
|
||||
- **Password**: Password
|
||||
|
||||
**Connection String Generata:**
|
||||
```
|
||||
Server=sql.example.com,1433;User Id=username;Password=***;Connection Timeout=30;Database=DBName;TrustServerCertificate=True
|
||||
```
|
||||
|
||||
### Scenario 5: SQL Server con Instance Name Remoto
|
||||
|
||||
**Configurazione Credenziale:**
|
||||
- **Host**: `server.domain.com\PRODUCTION`
|
||||
- **Porta**: 1433 (ignorata)
|
||||
- **Database**: Nome del database
|
||||
- **Username**: Utente SQL
|
||||
- **Password**: Password
|
||||
|
||||
**Connection String Generata:**
|
||||
```
|
||||
Server=server.domain.com\PRODUCTION;User Id=username;Password=***;Connection Timeout=30;Database=DBName;TrustServerCertificate=True
|
||||
```
|
||||
|
||||
## 🔍 Troubleshooting
|
||||
|
||||
### Problema: "A network-related or instance-specific error"
|
||||
|
||||
**Possibili Cause:**
|
||||
1. **SQL Server Browser non in esecuzione** (per named instances)
|
||||
- Soluzione: Avvia il servizio "SQL Server Browser" da services.msc
|
||||
|
||||
2. **TCP/IP non abilitato**
|
||||
- Soluzione: SQL Server Configuration Manager → Protocols → Enable TCP/IP
|
||||
|
||||
3. **Named Instance non specificata**
|
||||
- Soluzione: Usa `localhost\SQLEXPRESS` invece di solo `localhost`
|
||||
|
||||
4. **Firewall blocca la porta**
|
||||
- Soluzione: Aggiungi eccezione firewall per SQL Server
|
||||
|
||||
### Problema: "Login failed for user"
|
||||
|
||||
**Possibili Cause:**
|
||||
1. **Windows Authentication richiesta ma SQL Auth specificata**
|
||||
- Soluzione: Usa username `Integrated` o lascialo vuoto
|
||||
|
||||
2. **SQL Authentication non abilitata**
|
||||
- Soluzione: SQL Server Management Studio → Proprietà Server → Security → SQL Server and Windows Authentication mode
|
||||
|
||||
3. **Password errata**
|
||||
- Soluzione: Verifica la password
|
||||
|
||||
### Problema: "Cannot open database"
|
||||
|
||||
**Possibili Cause:**
|
||||
1. **Database non esiste**
|
||||
- Soluzione: Verifica il nome del database o lascia il campo vuoto per connetterti solo al server
|
||||
|
||||
2. **Permessi insufficienti**
|
||||
- Soluzione: Verifica che l'utente abbia accesso al database
|
||||
|
||||
## ✅ Test di Connessione
|
||||
|
||||
Dopo aver configurato la credenziale, usa il pulsante **"Testa Connessione"** per verificare:
|
||||
- ✅ Connection string corretta
|
||||
- ✅ SQL Server raggiungibile
|
||||
- ✅ Autenticazione riuscita
|
||||
- ✅ Database accessibile (se specificato)
|
||||
|
||||
Il test mostra:
|
||||
- Versione SQL Server
|
||||
- Host e porta usati
|
||||
- Database connesso
|
||||
- Timeout configurato
|
||||
|
||||
## 📝 Note Tecniche
|
||||
|
||||
### Differenze TCP/IP vs Named Pipes
|
||||
|
||||
**Named Pipes** (preferito per localhost):
|
||||
- Più veloce per connessioni locali
|
||||
- Non richiede SQL Server Browser
|
||||
- Usa IPC invece di network stack
|
||||
- Sintassi: `Server=localhost` o `Server=.`
|
||||
|
||||
**TCP/IP** (richiesto per remote):
|
||||
- Richiesto per connessioni remote
|
||||
- Richiede porta specifica
|
||||
- Richiede SQL Server Browser per named instances
|
||||
- Sintassi: `Server=hostname,port`
|
||||
|
||||
### Windows Authentication vs SQL Authentication
|
||||
|
||||
**Windows Authentication**:
|
||||
- ✅ Più sicuro (usa credenziali Windows)
|
||||
- ✅ No password nel codice
|
||||
- ✅ Single Sign-On
|
||||
- ❌ Richiede domain trust per remote
|
||||
|
||||
**SQL Authentication**:
|
||||
- ✅ Funziona sempre (anche cross-domain)
|
||||
- ✅ Credenziali specifiche per SQL Server
|
||||
- ❌ Password nel connection string
|
||||
- ❌ Deve essere abilitato in SQL Server
|
||||
|
||||
## 🔄 Retrocompatibilità
|
||||
|
||||
Le modifiche sono completamente retrocompatibili:
|
||||
- ✅ Connection string esistenti continuano a funzionare
|
||||
- ✅ Credenziali già salvate non richiedono modifiche
|
||||
- ✅ Comportamento default invariato per server remoti
|
||||
- ✅ Nessuna migrazione database richiesta
|
||||
|
||||
## 📊 Impatto Performance
|
||||
|
||||
**Miglioramenti**:
|
||||
- 🚀 Named Pipes più veloce di TCP/IP per localhost
|
||||
- 🚀 Riduzione overhead network stack
|
||||
- 🚀 Connection pooling più efficiente
|
||||
|
||||
**Nessun Impatto Negativo**:
|
||||
- ✅ Server remoti usano sempre TCP/IP (comportamento corretto)
|
||||
- ✅ Connection string ottimizzate per scenario specifico
|
||||
|
||||
---
|
||||
|
||||
**Sviluppatore**: Alessio Dalsanto
|
||||
**Issue**: Connessione localhost SQL Server
|
||||
**Status**: ✅ Risolto
|
||||
Reference in New Issue
Block a user