[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
+546 -44
View File
@@ -1,10 +1,13 @@
@page "/credentials"
@using System.Linq
@using CredentialManager.Models
@using CredentialManager.Services
@using DataConnection.CredentialManagement.Interfaces
@using DataConnection.CredentialManagement.Models
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.JSInterop
@inject IDataConnectionCredentialService CredentialService
@inject IOdbcDsnDiscoveryService OdbcDsnDiscoveryService
@inject IJSRuntime JSRuntime
@inject NavigationManager Navigation
@@ -37,7 +40,7 @@
<div class="row mb-3">
<div class="col">
<div class="btn-group" role="group">
<button class="btn btn-primary" @onclick="ShowAddDatabaseModal">
<button class="btn btn-primary" @onclick="async () => await ShowAddDatabaseModal()">
<i class="oi oi-plus"></i> Database
</button>
<button class="btn btn-secondary" @onclick="ShowAddRestApiModal">
@@ -109,7 +112,7 @@ else
</td>
<td>@credential.Username</td>
<td>
<button class="btn btn-sm btn-outline-primary" @onclick="() => EditDatabaseCredential(credential)">
<button class="btn btn-sm btn-outline-primary" @onclick="async () => await EditDatabaseCredential(credential)">
<i class="oi oi-pencil"></i>
</button>
<button class="btn btn-sm btn-outline-success ms-1" @onclick="() => TestDatabaseConnection(credential)">
@@ -229,53 +232,280 @@ else
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Tipo Database *</label>
<InputSelect class="form-select" @bind-Value="currentDatabaseCredential.DatabaseType">
<InputSelect class="form-select" @bind-Value="currentDatabaseCredential.DatabaseType"
@bind-Value:after="OnDatabaseTypeChangedAsync">
<option value="@CredentialManager.Models.DatabaseType.SqlServer">SQL Server</option>
<option value="@CredentialManager.Models.DatabaseType.MySql">MySQL</option>
@* <option value="@CredentialManager.Models.DatabaseType.MySql">MySQL</option>
<option value="@CredentialManager.Models.DatabaseType.PostgreSql">PostgreSQL</option>
<option value="@CredentialManager.Models.DatabaseType.Oracle">Oracle</option>
<option value="@CredentialManager.Models.DatabaseType.Sqlite">SQLite</option>
<option value="@CredentialManager.Models.DatabaseType.DB2">DB2</option>
<option value="@CredentialManager.Models.DatabaseType.SapHana">SAP HANA</option>
<option value="@CredentialManager.Models.DatabaseType.DB2">DB2</option>
<option value="@CredentialManager.Models.DatabaseType.SapHana">SAP HANA</option>*@
<option value="@CredentialManager.Models.DatabaseType.Odbc">ODBC</option>
</InputSelect>
</div>
</div>
</div>
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label class="form-label">Host *</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Host" />
@if (currentDatabaseCredential.DatabaseType == CredentialManager.Models.DatabaseType.Odbc)
{
<!-- Configurazione ODBC -->
<div class="card mb-3">
<div class="card-header bg-info text-white">
<h6 class="mb-0"><i class="oi oi-link-intact"></i> Configurazione ODBC</h6>
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Porta *</label>
<InputNumber class="form-control" @bind-Value="currentDatabaseCredential.Port" />
</div>
</div>
</div> <div class="mb-3">
<label class="form-label">Nome Database <small class="text-muted">(opzionale)</small></label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.DatabaseName"
placeholder="Lascia vuoto per connessione al server senza database specifico" />
<div class="form-text">Se non specificato, la connessione sarà al server senza selezionare un database specifico</div>
</div>
<div class="card-body">
<div class="mb-3">
<label class="form-label">Modalità Connessione *</label>
<select class="form-select" @bind="currentDatabaseCredential.OdbcMode">
<option value="@CredentialManager.Models.OdbcConnectionMode.Dsn">Utilizza DSN (Data Source Name)</option>
<option value="@CredentialManager.Models.OdbcConnectionMode.Custom">Connection String Personalizzata</option>
</select>
<small class="form-text text-muted">
@if (currentDatabaseCredential.OdbcMode == CredentialManager.Models.OdbcConnectionMode.Dsn)
{
<span>Seleziona un DSN ODBC configurato sul sistema</span>
}
else
{
<span>Crea una connection string personalizzata con guida passo-passo</span>
}
</small>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Username *</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Username" />
@if (currentDatabaseCredential.OdbcMode == CredentialManager.Models.OdbcConnectionMode.Dsn)
{
<!-- Modalità DSN -->
<div class="row">
<div class="col-md-12">
<div class="mb-3">
<label class="form-label">
Seleziona DSN *
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" @onclick="RefreshOdbcDsnList">
<i class="oi oi-reload"></i> Aggiorna Lista
</button>
</label>
<select class="form-select" @bind="currentDatabaseCredential.OdbcDsnName">
<option value="">-- Seleziona un DSN --</option>
@if (availableOdbcDsn.Any())
{
<optgroup label="DSN Utente">
@foreach (var dsn in availableOdbcDsn.Where(d => d.IsUserDsn))
{
<option value="@dsn.Name">@dsn.Name (@dsn.Driver)</option>
}
</optgroup>
<optgroup label="DSN di Sistema">
@foreach (var dsn in availableOdbcDsn.Where(d => !d.IsUserDsn))
{
<option value="@dsn.Name">@dsn.Name (@dsn.Driver)</option>
}
</optgroup>
}
else
{
<option disabled>Nessun DSN ODBC configurato</option>
}
</select>
@if (!string.IsNullOrEmpty(currentDatabaseCredential.OdbcDsnName))
{
var selectedDsn = availableOdbcDsn.FirstOrDefault(d => d.Name == currentDatabaseCredential.OdbcDsnName);
if (selectedDsn != null)
{
<div class="alert alert-info mt-2">
<strong>Driver:</strong> @selectedDsn.Driver<br />
@if (!string.IsNullOrEmpty(selectedDsn.Description))
{
<strong>Descrizione:</strong> @selectedDsn.Description<br />
}
<strong>Tipo:</strong> @(selectedDsn.IsUserDsn ? "DSN Utente" : "DSN di Sistema")
</div>
}
}
</div>
</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Username</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Username"
placeholder="Lascia vuoto se incluso nel DSN" />
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Password</label>
<InputText type="password" class="form-control" @bind-Value="currentDatabaseCredential.Password"
placeholder="Lascia vuoto se inclusa nel DSN" />
</div>
</div>
</div>
}
else
{
<!-- Modalità Custom Connection String Builder -->
<div class="alert alert-warning">
<i class="oi oi-info"></i> <strong>Costruzione Guidata Connection String</strong><br />
Compila i campi per costruire automaticamente la connection string ODBC.
</div>
<div class="mb-3">
<label class="form-label">
Driver ODBC *
<button type="button" class="btn btn-sm btn-outline-secondary ms-2" @onclick="RefreshOdbcDriverList">
<i class="oi oi-reload"></i> Aggiorna Lista
</button>
</label>
<select class="form-select" @bind="selectedOdbcDriver">
<option value="">-- Seleziona Driver --</option>
@foreach (var driver in availableOdbcDrivers)
{
<option value="@driver">@driver</option>
}
</select>
@if (!string.IsNullOrEmpty(selectedOdbcDriver))
{
<small class="form-text text-success">
<i class="oi oi-check"></i> Driver selezionato: @selectedOdbcDriver
</small>
}
</div>
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label class="form-label">Server/Host</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Host"
placeholder="es. localhost o 192.168.1.100" />
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Porta <small class="text-muted">(opzionale)</small></label>
<InputNumber class="form-control" @bind-Value="currentDatabaseCredential.Port"
placeholder="0 = default" />
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Nome Database</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.DatabaseName"
placeholder="es. mydatabase" />
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Username</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Username"
placeholder="Opzionale se incluso nel driver" />
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Password</label>
<InputText type="password" class="form-control" @bind-Value="currentDatabaseCredential.Password"
placeholder="Opzionale se inclusa nel driver" />
</div>
</div>
</div>
<!-- Parametri Personalizzati -->
<div class="mb-3">
<label class="form-label">
Parametri Personalizzati <small class="text-muted">(opzionale)</small>
<button type="button" class="btn btn-sm btn-success ms-2" @onclick="AddOdbcCustomParameter">
<i class="oi oi-plus"></i> Aggiungi
</button>
</label>
<small class="form-text text-muted d-block mb-2">
Aggiungi parametri aggiuntivi alla connection string (es. TrustServerCertificate=yes, Encrypt=no, etc.)
</small>
@if (currentDatabaseCredential.AdditionalParameters != null && currentDatabaseCredential.AdditionalParameters.Any())
{
@foreach (var param in currentDatabaseCredential.AdditionalParameters.Where(p => p.Key != "Driver").ToList())
{
<div class="input-group mb-2">
<input type="text" class="form-control" placeholder="Nome parametro"
value="@param.Key" @onchange="@(e => UpdateOdbcParameterKey(param.Key, e.Value?.ToString() ?? string.Empty))" />
<span class="input-group-text">=</span>
<input type="text" class="form-control" placeholder="Valore"
value="@param.Value" @onchange="@(e => UpdateOdbcParameterValue(param.Key, e.Value?.ToString() ?? string.Empty))" />
<button type="button" class="btn btn-outline-danger" @onclick="@(() => RemoveOdbcParameter(param.Key))">
<i class="oi oi-trash"></i>
</button>
</div>
}
}
else
{
<div class="alert alert-light small mb-0">
<i class="oi oi-info"></i> Nessun parametro personalizzato aggiunto
</div>
}
</div>
<!-- Anteprima Connection String -->
@if (!string.IsNullOrEmpty(selectedOdbcDriver) ||
!string.IsNullOrEmpty(currentDatabaseCredential.Host))
{
<div class="mb-3">
<label class="form-label">Anteprima Connection String</label>
<textarea class="form-control font-monospace" rows="3" readonly>@GetOdbcConnectionStringPreview()</textarea>
<small class="form-text text-muted">
Questa è un'anteprima della connection string che verrà generata
</small>
</div>
}
}
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Password *</label>
<InputText type="password" class="form-control" @bind-Value="currentDatabaseCredential.Password" />
}
else
{
<!-- Configurazione Standard Database -->
<div class="row">
<div class="col-md-8">
<div class="mb-3">
<label class="form-label">Host/Server *</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Host"
placeholder="es. localhost o server.dominio.com" />
</div>
</div>
<div class="col-md-4">
<div class="mb-3">
<label class="form-label">Porta *</label>
<InputNumber class="form-control" @bind-Value="currentDatabaseCredential.Port" />
</div>
</div>
</div>
</div>
<div class="mb-3">
<label class="form-label">Nome Database <small class="text-muted">(opzionale)</small></label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.DatabaseName"
placeholder="Lascia vuoto per connessione al server senza database specifico" />
<div class="form-text">Se non specificato, la connessione sarà al server senza selezionare un database specifico</div>
</div>
<div class="row">
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Username *</label>
<InputText class="form-control" @bind-Value="currentDatabaseCredential.Username" />
</div>
</div>
<div class="col-md-6">
<div class="mb-3">
<label class="form-label">Password *</label>
<InputText type="password" class="form-control" @bind-Value="currentDatabaseCredential.Password" />
</div>
</div>
</div>
}
<div class="row">
<div class="col-md-6">
@@ -596,6 +826,12 @@ else
private RestApiCredential? editingRestApiCredential = null;
private DatabaseCredential currentDatabaseCredential = new();
private RestApiCredential currentRestApiCredential = new();
// ODBC specific state
private List<OdbcDsnInfo> availableOdbcDsn = new();
private List<string> availableOdbcDrivers = new();
private string selectedOdbcDriver = string.Empty;
private bool loadingOdbcData = false;
protected override async Task OnInitializedAsync()
{ await RefreshCredentials();
@@ -626,19 +862,26 @@ else
#region Database Credential Methods
private void ShowAddDatabaseModal()
private async Task ShowAddDatabaseModal()
{
editingDatabaseCredential = null;
currentDatabaseCredential = new DatabaseCredential
{
DatabaseType = CredentialManager.Models.DatabaseType.SqlServer,
Port = 1433,
CommandTimeout = 30
CommandTimeout = 30,
AdditionalParameters = new Dictionary<string, string>()
};
showDatabaseModal = true;
// Se è ODBC, carica i dati automaticamente
if (currentDatabaseCredential.DatabaseType == DatabaseType.Odbc)
{
await LoadOdbcData();
}
}
private void EditDatabaseCredential(DatabaseCredential credential)
private async Task EditDatabaseCredential(DatabaseCredential credential)
{
editingDatabaseCredential = credential;
currentDatabaseCredential = new DatabaseCredential
@@ -651,8 +894,24 @@ else
Username = credential.Username,
Password = credential.Password,
CommandTimeout = credential.CommandTimeout,
IgnoreSslErrors = credential.IgnoreSslErrors
IgnoreSslErrors = credential.IgnoreSslErrors,
OdbcDsnName = credential.OdbcDsnName,
OdbcMode = credential.OdbcMode,
AdditionalParameters = credential.AdditionalParameters != null
? new Dictionary<string, string>(credential.AdditionalParameters)
: new Dictionary<string, string>()
};
// Se è ODBC, carica i dati e ripristina il driver selezionato
if (currentDatabaseCredential.DatabaseType == DatabaseType.Odbc)
{
await LoadOdbcData();
if (currentDatabaseCredential.AdditionalParameters?.ContainsKey("Driver") == true)
{
selectedOdbcDriver = currentDatabaseCredential.AdditionalParameters["Driver"];
}
}
showDatabaseModal = true;
}
@@ -697,16 +956,53 @@ else
testingConnection = true;
try
{
// Valida i campi obbligatori
if (string.IsNullOrEmpty(currentDatabaseCredential.Name) ||
string.IsNullOrEmpty(currentDatabaseCredential.Host) ||
string.IsNullOrEmpty(currentDatabaseCredential.Username) ||
string.IsNullOrEmpty(currentDatabaseCredential.Password))
// Validazione base: Nome sempre obbligatorio
if (string.IsNullOrEmpty(currentDatabaseCredential.Name))
{
await JSRuntime.InvokeVoidAsync("alert", "Compila tutti i campi obbligatori prima di testare la connessione.");
await JSRuntime.InvokeVoidAsync("alert", "Il nome della credenziale è obbligatorio.");
return;
}
// Validazione specifica per tipo database
if (currentDatabaseCredential.DatabaseType == DatabaseType.Odbc)
{
// ODBC: Validazione in base alla modalità
if (currentDatabaseCredential.OdbcMode == OdbcConnectionMode.Dsn)
{
// Modalità DSN: richiede DSN selezionato
if (string.IsNullOrEmpty(currentDatabaseCredential.OdbcDsnName))
{
await JSRuntime.InvokeVoidAsync("alert", "Seleziona un DSN ODBC.");
return;
}
}
else
{
// Modalità Custom: richiede driver e host
if (!currentDatabaseCredential.AdditionalParameters?.ContainsKey("Driver") ?? true)
{
await JSRuntime.InvokeVoidAsync("alert", "Seleziona un driver ODBC.");
return;
}
if (string.IsNullOrEmpty(currentDatabaseCredential.Host))
{
await JSRuntime.InvokeVoidAsync("alert", "Inserisci il server/host.");
return;
}
}
}
else
{
// Altri database: validazione standard (Host, Username, Password)
if (string.IsNullOrEmpty(currentDatabaseCredential.Host) ||
string.IsNullOrEmpty(currentDatabaseCredential.Username) ||
string.IsNullOrEmpty(currentDatabaseCredential.Password))
{
await JSRuntime.InvokeVoidAsync("alert", "Compila tutti i campi obbligatori (Host, Username, Password).");
return;
}
}
var (success, message) = await CredentialService.TestDatabaseConnectionAsync(currentDatabaseCredential);
var title = success ? "Test Connessione - Successo" : "Test Connessione - Errore";
@@ -722,6 +1018,212 @@ else
}
}
#region ODBC Methods
/// <summary>
/// Gestisce il cambio di tipo database per caricare le liste ODBC quando necessario
/// </summary>
private async Task OnDatabaseTypeChangedAsync()
{
// Se è ODBC, carica le liste DSN e driver
if (currentDatabaseCredential.DatabaseType == DatabaseType.Odbc)
{
await LoadOdbcData();
}
StateHasChanged();
}
/// <summary>
/// Carica i dati ODBC (DSN e driver disponibili)
/// </summary>
private async Task LoadOdbcData()
{
if (loadingOdbcData) return;
loadingOdbcData = true;
try
{
await Task.Run(() =>
{
try
{
availableOdbcDsn = OdbcDsnDiscoveryService.GetAllDsn();
availableOdbcDrivers = OdbcDsnDiscoveryService.GetInstalledDrivers();
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel caricamento dati ODBC: {ex.Message}");
availableOdbcDsn = new List<OdbcDsnInfo>();
availableOdbcDrivers = new List<string>();
}
});
}
finally
{
loadingOdbcData = false;
StateHasChanged();
}
}
/// <summary>
/// Ricarica manualmente la lista dei DSN ODBC
/// </summary>
private async Task RefreshOdbcDsnList()
{
await LoadOdbcData();
await JSRuntime.InvokeVoidAsync("alert", $"Lista DSN aggiornata: {availableOdbcDsn.Count} DSN trovati");
}
/// <summary>
/// Ricarica manualmente la lista dei driver ODBC
/// </summary>
private async Task RefreshOdbcDriverList()
{
await LoadOdbcData();
await JSRuntime.InvokeVoidAsync("alert", $"Lista driver aggiornata: {availableOdbcDrivers.Count} driver trovati");
}
/// <summary>
/// Genera l'anteprima della stringa di connessione ODBC
/// </summary>
private string GetOdbcConnectionStringPreview()
{
if (currentDatabaseCredential.DatabaseType != DatabaseType.Odbc)
return string.Empty;
try
{
// Salva il driver selezionato nei parametri aggiuntivi temporaneamente
if (!string.IsNullOrEmpty(selectedOdbcDriver))
{
currentDatabaseCredential.AdditionalParameters ??= new Dictionary<string, string>();
currentDatabaseCredential.AdditionalParameters["Driver"] = selectedOdbcDriver;
}
// Usa il metodo di ConnectionStringBuilder per generare la stringa
return ConnectionStringBuilder.BuildConnectionString(currentDatabaseCredential);
}
catch (Exception ex)
{
return $"Errore nella generazione: {ex.Message}";
}
}
/// <summary>
/// Gestisce la selezione di un DSN dalla lista
/// </summary>
private void OnOdbcDsnSelected(ChangeEventArgs e)
{
var dsnName = e.Value?.ToString();
if (!string.IsNullOrEmpty(dsnName))
{
currentDatabaseCredential.OdbcDsnName = dsnName;
StateHasChanged();
}
}
/// <summary>
/// Gestisce il cambio di modalità ODBC (DSN vs Custom)
/// </summary>
private void OnOdbcModeChanged(ChangeEventArgs e)
{
if (Enum.TryParse<OdbcConnectionMode>(e.Value?.ToString(), out var mode))
{
currentDatabaseCredential.OdbcMode = mode;
StateHasChanged();
}
}
/// <summary>
/// Ottiene i dettagli di un DSN selezionato
/// </summary>
private OdbcDsnInfo? GetSelectedDsnDetails()
{
if (string.IsNullOrEmpty(currentDatabaseCredential.OdbcDsnName))
return null;
return availableOdbcDsn.FirstOrDefault(dsn =>
dsn.Name.Equals(currentDatabaseCredential.OdbcDsnName, StringComparison.OrdinalIgnoreCase));
}
/// <summary>
/// Aggiunge un nuovo parametro personalizzato ODBC
/// </summary>
private void AddOdbcCustomParameter()
{
currentDatabaseCredential.AdditionalParameters ??= new Dictionary<string, string>();
// Genera un nome univoco per il nuovo parametro
var index = 1;
var paramName = $"Param{index}";
while (currentDatabaseCredential.AdditionalParameters.ContainsKey(paramName))
{
index++;
paramName = $"Param{index}";
}
currentDatabaseCredential.AdditionalParameters[paramName] = string.Empty;
StateHasChanged();
}
/// <summary>
/// Aggiorna la chiave di un parametro personalizzato
/// </summary>
private void UpdateOdbcParameterKey(string oldKey, string newKey)
{
if (string.IsNullOrWhiteSpace(newKey) || oldKey == newKey)
return;
if (currentDatabaseCredential.AdditionalParameters == null)
return;
// Se la nuova chiave esiste già, non fare nulla
if (currentDatabaseCredential.AdditionalParameters.ContainsKey(newKey))
{
StateHasChanged();
return;
}
var value = currentDatabaseCredential.AdditionalParameters[oldKey];
currentDatabaseCredential.AdditionalParameters.Remove(oldKey);
currentDatabaseCredential.AdditionalParameters[newKey] = value;
StateHasChanged();
}
/// <summary>
/// Aggiorna il valore di un parametro personalizzato
/// </summary>
private void UpdateOdbcParameterValue(string key, string value)
{
if (currentDatabaseCredential.AdditionalParameters == null)
return;
if (currentDatabaseCredential.AdditionalParameters.ContainsKey(key))
{
currentDatabaseCredential.AdditionalParameters[key] = value;
StateHasChanged();
}
}
/// <summary>
/// Rimuove un parametro personalizzato
/// </summary>
private void RemoveOdbcParameter(string key)
{
if (currentDatabaseCredential.AdditionalParameters == null)
return;
// Non permettere la rimozione del parametro Driver
if (key == "Driver")
return;
currentDatabaseCredential.AdditionalParameters.Remove(key);
StateHasChanged();
}
#endregion
#endregion
#region REST API Credential Methods
+3
View File
@@ -106,6 +106,9 @@ builder.Services.AddHttpClient();
// Register Data Connection Factory
builder.Services.AddScoped<IDataConnectionFactory, DataConnectionFactory>();
// Register ODBC DSN Discovery Service
builder.Services.AddScoped<CredentialManager.Services.IOdbcDsnDiscoveryService, CredentialManager.Services.OdbcDsnDiscoveryService>();
// Register Association Service (Pre-Discovery)
builder.Services.AddScoped<Data_Coupler.Services.IAssociationService, Data_Coupler.Services.AssociationService>();
@@ -75,7 +75,15 @@ namespace Data_Coupler.Services
{
throw new ArgumentException($"Credenziale database '{credentialName}' non trovata");
}
// Per ODBC, usa OdbcDatabaseManager direttamente (EF Core non supporta ODBC)
if (credential.DatabaseType == DatabaseType.Odbc)
{
var connectionString = CredentialManager.Models.ConnectionStringBuilder.BuildConnectionString(credential);
_logger.LogInformation("Creando OdbcDatabaseManager con connection string per {CredentialName}", credentialName);
return new DataConnection.DB.OdbcDatabaseManager(connectionString);
}
// Per altri database, usa EFCoreDatabaseManager
var dbManagerOptions = await _credentialService.GetDbManagerOptionsAsync(credential.Name);
return new EFCoreDatabaseManager(dbManagerOptions);
}