Implementato il supporto per la scoperta e la visualizzazione dello schema del database, inclusa la creazione di provider specifici per SQL Server e l'integrazione con il servizio di connessione al database.

This commit is contained in:
2025-04-28 23:55:56 +02:00
parent 503b567fb7
commit d1103c4e7d
10 changed files with 546 additions and 79 deletions
+43 -1
View File
@@ -6,6 +6,7 @@ using DataConnection.Interfaces;
using DataConnection.EF;
using DataConnection.EF.DatabaseDiscovery;
using DataConnection.Enums;
using Microsoft.EntityFrameworkCore;
namespace Data_Coupler.Data
{
@@ -13,6 +14,8 @@ namespace Data_Coupler.Data
{
private readonly IDatabaseDiscovery _sqlServerDiscovery;
private readonly IDatabaseDiscovery _mySqlDiscovery;
private readonly DbManagerOptions _dbManagerOptions;
private IDatabaseManager _databaseManager;
public DatabaseType SelectedDatabaseType { get; set; } = DatabaseType.SqlServer;
public string ConnectionString { get; set; }
@@ -22,9 +25,10 @@ namespace Data_Coupler.Data
public bool IsConnected { get; private set; }
public string ErrorMessage { get; private set; }
public DatabaseConnectionService()
public DatabaseConnectionService(DbManagerOptions dbManagerOptions)
{
_sqlServerDiscovery = new SqlServerDatabaseDiscovery();
_dbManagerOptions = dbManagerOptions ?? new DbManagerOptions();
// Se in futuro verrà implementata la discovery MySQL, potremmo aggiungerla qui
// _mySqlDiscovery = new MySqlDatabaseDiscovery();
}
@@ -104,5 +108,43 @@ namespace Data_Coupler.Data
// Per altri tipi di database, implementare la logica appropriata
return ConnectionString;
}
public IDatabaseManager GetDatabaseManager()
{
if (_databaseManager != null)
return _databaseManager;
// Configura le opzioni del database manager
_dbManagerOptions.ServerConnectionString = ConnectionString;
_dbManagerOptions.DatabaseName = SelectedDatabase;
// Configura il contesto in base al tipo di database
_dbManagerOptions.DbContextConfigurator = options =>
{
switch (SelectedDatabaseType)
{
case DatabaseType.SqlServer:
options.UseSqlServer(BuildConnectionStringWithDatabase(),
sqlOptions => sqlOptions.CommandTimeout(_dbManagerOptions.CommandTimeout));
break;
// Aggiungi altri tipi di database quando implementati
default:
throw new NotSupportedException($"Tipo di database non supportato: {SelectedDatabaseType}");
}
};
// Crea il database manager
_databaseManager = new EFCoreDatabaseManager(_dbManagerOptions);
return _databaseManager;
}
public void DisposeDatabaseManager()
{
if (_databaseManager != null)
{
_databaseManager.Dispose();
_databaseManager = null;
}
}
}
}
+11 -38
View File
@@ -241,17 +241,19 @@
databaseInfo = dbInfo;
}
// Qui dovresti implementare la connessione al database selezionato
// e recuperare le informazioni sullo schema
// Per questo esempio, simuliamo che sia avvenuto con successo
await Task.Delay(1000); // Simulazione operazione asincrona
// Crea il database manager e connettiti al database selezionato
var databaseManager = DatabaseService.GetDatabaseManager();
// Verifica la connessione al database specifico
if (!await databaseManager.TestConnectionAsync())
{
throw new Exception($"Non è possibile connettersi al database {DatabaseService.SelectedDatabase}");
}
// Recupera le informazioni sullo schema (tabelle e colonne)
schemaInfo = await databaseManager.GetDatabaseSchemaAsync();
showSchemaInfo = true;
// In un'implementazione reale, qui dovresti eseguire:
// schemaInfo = await databaseManager.GetDatabaseSchemaAsync();
// Per ora creiamo dati di esempio
schemaInfo = CreateSampleSchemaInfo();
}
catch (Exception ex)
{
@@ -264,33 +266,4 @@
isConnecting = false;
}
}
// Metodo temporaneo per generare dati di esempio - da sostituire con dati reali
private IDictionary<string, IEnumerable<DbColumnInfo>> CreateSampleSchemaInfo()
{
var result = new Dictionary<string, IEnumerable<DbColumnInfo>>();
// Tabella Customers
var customerColumns = new List<DbColumnInfo>
{
new DbColumnInfo { Name = "Id", DataType = "int", IsPrimaryKey = true },
new DbColumnInfo { Name = "Name", DataType = "nvarchar(100)" },
new DbColumnInfo { Name = "Email", DataType = "nvarchar(255)" },
new DbColumnInfo { Name = "Created", DataType = "datetime" }
};
// Tabella Orders
var orderColumns = new List<DbColumnInfo>
{
new DbColumnInfo { Name = "Id", DataType = "int", IsPrimaryKey = true },
new DbColumnInfo { Name = "CustomerId", DataType = "int", IsForeignKey = true, ReferencedTable = "Customers", ReferencedColumn = "Id" },
new DbColumnInfo { Name = "OrderDate", DataType = "datetime" },
new DbColumnInfo { Name = "TotalAmount", DataType = "decimal(18,2)" }
};
result.Add("Customers", customerColumns);
result.Add("Orders", orderColumns);
return result;
}
}
+266
View File
@@ -0,0 +1,266 @@
@page "/database-schema"
@using DataConnection.Interfaces
@using DataConnection.Enums
@inject Data.DatabaseConnectionService DatabaseService
@inject IJSRuntime JSRuntime
@inject NavigationManager NavigationManager
<PageTitle>Schema del Database</PageTitle>
<h3>Schema del Database</h3>
@if (!DatabaseService.IsConnected || string.IsNullOrEmpty(DatabaseService.SelectedDatabase))
{
<div class="alert alert-warning">
<p>Non sei connesso a nessun database. Devi prima stabilire una connessione.</p>
<button class="btn btn-primary" @onclick="NavigateToConnection">Vai alla pagina di connessione</button>
</div>
}
else
{
<div class="row mb-4">
<div class="col-md-12">
<div class="card">
<div class="card-header d-flex justify-content-between align-items-center">
<span>Dettagli del Database: <strong>@DatabaseService.SelectedDatabase</strong></span>
<button class="btn btn-outline-primary btn-sm" @onclick="RefreshSchema">
<i class="oi oi-reload"></i> Aggiorna Schema
</button>
</div>
<div class="card-body">
@if (databaseInfo != null)
{
<div class="row">
<div class="col-md-3">
<strong>Nome:</strong> @databaseInfo.Name
</div>
<div class="col-md-3">
<strong>Dimensione:</strong> @databaseInfo.SizeMB.ToString("N2") MB
</div>
<div class="col-md-3">
<strong>Creato il:</strong> @databaseInfo.CreationDate.ToString("dd/MM/yyyy HH:mm")
</div>
<div class="col-md-3">
<strong>Stato:</strong> @databaseInfo.Status
</div>
</div>
}
</div>
</div>
</div>
</div>
<div class="row">
<div class="col-md-3">
<div class="card">
<div class="card-header">
Tabelle
</div>
<div class="card-body p-0">
<div class="list-group list-group-flush">
<input type="text" class="form-control border-0 mb-2" placeholder="Filtra tabelle..." @oninput="FilterTables" />
@if (schemaInfo != null && schemaInfo.Count > 0)
{
@foreach (var table in filteredTables)
{
<button class="list-group-item list-group-item-action @(selectedTable == table.Key ? "active" : "")"
@onclick="() => SelectTable(table.Key)">
@table.Key
</button>
}
}
else if (isLoading)
{
<div class="text-center p-3">
<div class="spinner-border" role="status">
<span class="visually-hidden">Caricamento...</span>
</div>
</div>
}
else
{
<div class="list-group-item text-center text-muted">
Nessuna tabella disponibile
</div>
}
</div>
</div>
</div>
</div>
<div class="col-md-9">
<div class="card">
<div class="card-header">
@if (!string.IsNullOrEmpty(selectedTable))
{
<span>Struttura della tabella: <strong>@selectedTable</strong></span>
}
else
{
<span>Seleziona una tabella</span>
}
</div>
<div class="card-body">
@if (!string.IsNullOrEmpty(selectedTable) && schemaInfo != null && schemaInfo.ContainsKey(selectedTable))
{
<div class="table-responsive">
<table class="table table-bordered table-hover">
<thead class="table-light">
<tr>
<th>Nome Colonna</th>
<th>Tipo Dati</th>
<th>Nullable</th>
<th>Chiave</th>
<th>Relazioni</th>
</tr>
</thead>
<tbody>
@foreach (var column in schemaInfo[selectedTable])
{
<tr>
<td>@column.Name</td>
<td>@column.DataType</td>
<td>
@if (column.IsNullable)
{
<span class="badge bg-secondary">NULL</span>
}
else
{
<span class="badge bg-danger">NOT NULL</span>
}
</td>
<td>
@if (column.IsPrimaryKey)
{
<span class="badge bg-primary">PK</span>
}
</td>
<td>
@if (column.IsForeignKey)
{
<span class="badge bg-info text-dark">
FK → @column.ReferencedTable.@column.ReferencedColumn
</span>
}
</td>
</tr>
}
</tbody>
</table>
</div>
}
else if (string.IsNullOrEmpty(selectedTable))
{
<div class="alert alert-info">
Seleziona una tabella dal menu a sinistra per visualizzarne la struttura.
</div>
}
else if (isLoading)
{
<div class="text-center">
<div class="spinner-border" role="status">
<span class="visually-hidden">Caricamento...</span>
</div>
</div>
}
else
{
<div class="alert alert-warning">
Impossibile trovare informazioni sulla tabella selezionata.
</div>
}
</div>
</div>
</div>
</div>
}
@code {
private bool isLoading = false;
private string selectedTable = "";
private string tableFilter = "";
private DatabaseInfo databaseInfo;
private IDictionary<string, IEnumerable<DbColumnInfo>> schemaInfo;
private IDictionary<string, IEnumerable<DbColumnInfo>> filteredTables;
protected override async Task OnInitializedAsync()
{
if (DatabaseService.IsConnected && !string.IsNullOrEmpty(DatabaseService.SelectedDatabase))
{
await LoadDatabaseSchema();
}
}
private async Task LoadDatabaseSchema()
{
try
{
isLoading = true;
selectedTable = "";
// Ottieni informazioni sul database selezionato
if (DatabaseService.DatabasesInfo.TryGetValue(DatabaseService.SelectedDatabase, out var dbInfo))
{
databaseInfo = dbInfo;
}
// Crea il database manager e connettiti al database selezionato
var databaseManager = DatabaseService.GetDatabaseManager();
// Verifica la connessione al database specifico
if (await databaseManager.TestConnectionAsync())
{
// Recupera le informazioni sullo schema (tabelle e colonne)
schemaInfo = await databaseManager.GetDatabaseSchemaAsync();
filteredTables = schemaInfo;
}
else
{
throw new Exception($"Non è possibile connettersi al database {DatabaseService.SelectedDatabase}");
}
}
catch (Exception ex)
{
await JSRuntime.InvokeVoidAsync("console.error", "Errore caricamento schema:", ex.Message);
//DatabaseService.ErrorMessage = $"Errore nel caricamento dello schema: {ex.Message}";
schemaInfo = null;
}
finally
{
isLoading = false;
}
}
private void SelectTable(string tableName)
{
selectedTable = tableName;
}
private void FilterTables(ChangeEventArgs e)
{
tableFilter = e.Value?.ToString() ?? "";
if (string.IsNullOrWhiteSpace(tableFilter))
{
filteredTables = schemaInfo;
}
else
{
filteredTables = schemaInfo
.Where(t => t.Key.Contains(tableFilter, StringComparison.OrdinalIgnoreCase))
.ToDictionary(t => t.Key, t => t.Value);
}
}
private async Task RefreshSchema()
{
await LoadDatabaseSchema();
}
private void NavigateToConnection()
{
NavigationManager.NavigateTo("/database-connection");
}
}
+5
View File
@@ -29,6 +29,11 @@
<span class="oi oi-hard-drive" aria-hidden="true"></span> Database
</NavLink>
</div>
<div class="nav-item px-3">
<NavLink class="nav-link" href="database-schema">
<span class="oi oi-list" aria-hidden="true"></span> Struttura Database
</NavLink>
</div>
</nav>
</div>