[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:
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user