[Feature] Implementazione completa supporto ODBC

- Aggiunta persistenza campi ODBC (OdbcDsnName, OdbcMode) in CredentialEntity
- Creata migration EF Core per nuovi campi database
- Aggiornato mapping credenziali per caricare/salvare dati ODBC
- Creato OdbcDatabaseManager dedicato (bypass EF Core che non supporta ODBC)
- Aggiornato DataConnectionFactory per usare OdbcDatabaseManager con connessioni ODBC
- Fix auto-load DSN: sostituito @onchange con @bind-Value:after in dropdown tipo database
- Fix test connessione SAP HANA: rimossa query SELECT 1 che causava errori sintassi
- Implementati tutti i metodi IDatabaseManager in OdbcDatabaseManager
- Supporto completo per discovery schema, tabelle e query ODBC

Risolve problema DbContext non configurato per ODBC e abilita connessioni ODBC complete.
This commit is contained in:
Alessio Dal Santo
2026-02-02 18:24:44 +01:00
parent e7fb9a5cc7
commit 01f78466df
22 changed files with 3615 additions and 49 deletions
@@ -61,6 +61,13 @@ public class CredentialEntity
[MaxLength(2000)]
public string? AdditionalParameters { get; set; } // JSON per parametri aggiuntivi
// ODBC specific fields
[MaxLength(100)]
public string? OdbcDsnName { get; set; } // Nome del DSN ODBC configurato
[MaxLength(20)]
public string? OdbcMode { get; set; } // Dsn o Custom (OdbcConnectionMode enum)
public DateTime CreatedAt { get; set; } = DateTime.UtcNow;
public DateTime? UpdatedAt { get; set; }
+91 -1
View File
@@ -33,7 +33,24 @@ public enum DatabaseType
Oracle,
Sqlite,
DB2,
SapHana
SapHana,
Odbc
}
/// <summary>
/// Modalità di connessione ODBC
/// </summary>
public enum OdbcConnectionMode
{
/// <summary>
/// Utilizzo di un DSN (Data Source Name) configurato
/// </summary>
Dsn,
/// <summary>
/// Costruzione manuale della connection string
/// </summary>
Custom
}
/// <summary>
@@ -52,6 +69,10 @@ public class DatabaseCredential
public int CommandTimeout { get; set; } = 30;
public bool IgnoreSslErrors { get; set; } = false;
public Dictionary<string, string>? AdditionalParameters { get; set; }
// ODBC specific properties
public string? OdbcDsnName { get; set; } // Nome del DSN ODBC (se utilizzato)
public OdbcConnectionMode OdbcMode { get; set; } = OdbcConnectionMode.Dsn; // Modalità ODBC (DSN o Custom)
}
/// <summary>
@@ -148,6 +169,7 @@ public static class ConnectionStringBuilder
DatabaseType.Sqlite => BuildSqliteConnectionString(credential),
DatabaseType.DB2 => BuildDb2ConnectionString(credential),
DatabaseType.SapHana => BuildSapHanaConnectionString(credential),
DatabaseType.Odbc => BuildOdbcConnectionString(credential),
_ => throw new NotSupportedException($"Database type {credential.DatabaseType} not supported")
};
} private static string BuildSqlServerConnectionString(DatabaseCredential credential)
@@ -275,6 +297,74 @@ public static class ConnectionStringBuilder
return string.Join(";", builder);
}
private static string BuildOdbcConnectionString(DatabaseCredential credential)
{
// Se è già presente una connection string personalizzata, utilizzala
if (!string.IsNullOrEmpty(credential.ConnectionString))
return credential.ConnectionString;
var builder = new List<string>();
// Modalità DSN: usa il DSN configurato
if (credential.OdbcMode == OdbcConnectionMode.Dsn && !string.IsNullOrEmpty(credential.OdbcDsnName))
{
builder.Add($"DSN={credential.OdbcDsnName}");
// Aggiungi credenziali se fornite
if (!string.IsNullOrEmpty(credential.Username))
builder.Add($"UID={credential.Username}");
if (!string.IsNullOrEmpty(credential.Password))
builder.Add($"PWD={credential.Password}");
}
// Modalità Custom: costruisci manualmente la connection string
else
{
// Driver (se specificato nei parametri aggiuntivi)
if (credential.AdditionalParameters?.ContainsKey("Driver") == true)
{
builder.Add($"Driver={{{credential.AdditionalParameters["Driver"]}}}");
}
// Server/Host
if (!string.IsNullOrEmpty(credential.Host))
{
builder.Add($"Server={credential.Host}");
// Porta (se diversa da 0)
if (credential.Port > 0)
builder.Add($"Port={credential.Port}");
}
// Database
if (!string.IsNullOrEmpty(credential.DatabaseName))
builder.Add($"Database={credential.DatabaseName}");
// Credenziali
if (!string.IsNullOrEmpty(credential.Username))
builder.Add($"UID={credential.Username}");
if (!string.IsNullOrEmpty(credential.Password))
builder.Add($"PWD={credential.Password}");
}
// Timeout
if (credential.CommandTimeout > 0)
builder.Add($"Connection Timeout={credential.CommandTimeout}");
// Parametri aggiuntivi (escludendo Driver se già aggiunto)
if (credential.AdditionalParameters != null)
{
foreach (var param in credential.AdditionalParameters)
{
if (param.Key != "Driver") // Driver già gestito sopra
builder.Add($"{param.Key}={param.Value}");
}
}
return string.Join(";", builder);
}
private static void AddAdditionalParameters(List<string> builder, Dictionary<string, string>? additionalParams)
{
if (additionalParams != null)