483eb7b407
- 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
346 lines
11 KiB
Markdown
346 lines
11 KiB
Markdown
# 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
|