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
@@ -0,0 +1,33 @@
using System;
using DataConnection.EF.SchemaProviders;
using DataConnection.Enums;
using DataConnection.Interfaces;
namespace DataConnection.EF;
/// <summary>
/// Factory per la creazione di provider di schema del database
/// </summary>
public class DatabaseSchemaProviderFactory
{
/// <summary>
/// Crea un provider di schema in base al tipo di database
/// </summary>
/// <param name="databaseType">Tipo di database</param>
/// <returns>Provider di schema appropriato</returns>
public static IDatabaseSchemaProvider CreateProvider(DatabaseType databaseType)
{
return databaseType switch
{
DatabaseType.SqlServer => new SqlServerSchemaProvider(),
// Aggiungere qui altri provider quando implementati
// DatabaseType.MySql => new MySqlSchemaProvider(),
// DatabaseType.PostgreSql => new PostgreSqlSchemaProvider(),
// DatabaseType.Oracle => new OracleSchemaProvider(),
// DatabaseType.Sqlite => new SqliteSchemaProvider(),
// DatabaseType.DB2 => new DB2SchemaProvider(),
// DatabaseType.SapHana => new SapHanaSchemaProvider(),
_ => throw new NotSupportedException($"Tipo di database non supportato per l'estrazione dello schema: {databaseType}")
};
}
}
+7
View File
@@ -61,12 +61,19 @@ public class DbManagerOptions
/// </summary>
public string DatabaseName { get; set; }
/// <summary>
/// Tipo di database (SqlServer, MySql, ecc.)
/// </summary>
public DatabaseType DatabaseType { get; set; } = DatabaseType.SqlServer;
/// <summary>
/// Configura automaticamente il servizio di scoperta database in base al tipo di database
/// </summary>
/// <param name="databaseType">Tipo di database</param>
public void ConfigureDatabaseDiscovery(DatabaseType databaseType)
{
DatabaseType = databaseType;
switch (databaseType)
{
case DatabaseType.SqlServer:
+12 -40
View File
@@ -106,50 +106,22 @@ public class EFCoreDatabaseManager : IDatabaseManager
public async Task<IDictionary<string, IEnumerable<DbColumnInfo>>> GetDatabaseSchemaAsync()
{
// Assicurarsi che il contesto sia connesso
await _context.Database.OpenConnectionAsync();
var result = new Dictionary<string, IEnumerable<DbColumnInfo>>();
// Ottiene tutte le entità dal modello
var model = _context.Model;
var entityTypes = model.GetEntityTypes().ToList();
foreach (var entityType in entityTypes)
try
{
var tableName = entityType.GetTableName();
var schemaName = entityType.GetSchema();
var fullTableName = string.IsNullOrEmpty(schemaName) ? tableName : $"{schemaName}.{tableName}";
// Assicurarsi che il contesto sia connesso
await _context.Database.OpenConnectionAsync();
var columns = new List<DbColumnInfo>();
// Usa la factory per ottenere il provider appropriato in base al tipo di database
var schemaProvider = DatabaseSchemaProviderFactory.CreateProvider(_options.DatabaseType);
foreach (var property in entityType.GetProperties())
{
var columnInfo = new DbColumnInfo
{
Name = property.GetColumnName(),
DataType = property.GetColumnType(),
IsNullable = property.IsNullable,
IsPrimaryKey = property.IsPrimaryKey()
};
// Verifica se è una foreign key
var foreignKeys = entityType.GetForeignKeys().Where(fk => fk.Properties.Contains(property));
if (foreignKeys.Any())
{
var fk = foreignKeys.First();
columnInfo.IsForeignKey = true;
columnInfo.ReferencedTable = fk.PrincipalEntityType.GetTableName();
columnInfo.ReferencedColumn = fk.PrincipalKey.Properties.First().GetColumnName();
}
columns.Add(columnInfo);
}
result.Add(fullTableName, columns);
// Usa il provider per ottenere lo schema
return await schemaProvider.GetDatabaseSchemaAsync(_context.Database.GetConnectionString());
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel recupero dello schema del database: {ex.Message}");
throw;
}
return result;
}
public void Dispose()
@@ -0,0 +1,152 @@
using System;
using System.Collections.Generic;
using System.Data;
using System.Threading.Tasks;
using DataConnection.Interfaces;
using Microsoft.Data.SqlClient;
namespace DataConnection.EF.SchemaProviders;
/// <summary>
/// Provider di schema per database SQL Server
/// </summary>
public class SqlServerSchemaProvider : IDatabaseSchemaProvider
{
public async Task<IDictionary<string, IEnumerable<DbColumnInfo>>> GetDatabaseSchemaAsync(string connectionString)
{
var result = new Dictionary<string, IEnumerable<DbColumnInfo>>();
try
{
using (var connection = new SqlConnection(connectionString))
{
await connection.OpenAsync();
// Query per ottenere la struttura delle tabelle in SQL Server
string sql = @"
SELECT
SCHEMA_NAME(t.schema_id) + '.' + t.name AS TableName,
c.name AS ColumnName,
tp.name AS DataType,
c.max_length,
c.precision,
c.scale,
c.is_nullable,
CASE WHEN pk.column_id IS NOT NULL THEN 1 ELSE 0 END AS IsPrimaryKey,
CASE WHEN fk.parent_column_id IS NOT NULL THEN 1 ELSE 0 END AS IsForeignKey,
SCHEMA_NAME(ref_t.schema_id) + '.' + ref_t.name AS ReferencedTable,
ref_c.name AS ReferencedColumn
FROM
sys.tables t
INNER JOIN
sys.columns c ON t.object_id = c.object_id
INNER JOIN
sys.types tp ON c.user_type_id = tp.user_type_id
LEFT JOIN
(SELECT
ic.object_id,
ic.column_id
FROM
sys.indexes i
INNER JOIN
sys.index_columns ic ON i.object_id = ic.object_id AND i.index_id = ic.index_id
WHERE
i.is_primary_key = 1) pk ON t.object_id = pk.object_id AND c.column_id = pk.column_id
LEFT JOIN
sys.foreign_key_columns fk ON t.object_id = fk.parent_object_id AND c.column_id = fk.parent_column_id
LEFT JOIN
sys.tables ref_t ON fk.referenced_object_id = ref_t.object_id
LEFT JOIN
sys.columns ref_c ON fk.referenced_object_id = ref_c.object_id AND fk.referenced_column_id = ref_c.column_id
WHERE
t.is_ms_shipped = 0
ORDER BY
TableName, c.column_id";
using (var command = new SqlCommand(sql, connection))
{
command.CommandType = CommandType.Text;
using (var reader = await command.ExecuteReaderAsync())
{
string currentTable = null;
List<DbColumnInfo> columns = null;
while (await reader.ReadAsync())
{
string tableName = reader.GetString(0);
// Se stiamo passando a una nuova tabella, aggiungiamo la tabella precedente e creiamo una nuova lista
if (currentTable != tableName)
{
if (currentTable != null && columns != null && columns.Count > 0)
{
result[currentTable] = columns;
}
currentTable = tableName;
columns = new List<DbColumnInfo>();
}
// Formato del tipo di dati con precisione e scala per tipi numerici o lunghezza per tipi stringa
string dataType = reader.GetString(2);
int maxLength = reader.GetInt16(3);
byte precision = reader.GetByte(4);
byte scale = reader.GetByte(5);
// Formattazione tipo di dati per SQL Server
string formattedDataType = FormatSqlServerDataType(dataType, maxLength, precision, scale);
var columnInfo = new DbColumnInfo
{
Name = reader.GetString(1),
DataType = formattedDataType,
IsNullable = reader.GetBoolean(6),
IsPrimaryKey = reader.GetInt32(7) == 1,
IsForeignKey = reader.GetInt32(8) == 1,
ReferencedTable = reader.IsDBNull(9) ? null : reader.GetString(9),
ReferencedColumn = reader.IsDBNull(10) ? null : reader.GetString(10)
};
columns?.Add(columnInfo);
}
// Aggiungiamo l'ultima tabella
if (currentTable != null && columns != null && columns.Count > 0)
{
result[currentTable] = columns;
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel recupero dello schema del database SQL Server: {ex.Message}");
throw;
}
return result;
}
private static string FormatSqlServerDataType(string dataType, int maxLength, byte precision, byte scale)
{
string formattedDataType = dataType;
if (dataType == "nvarchar" || dataType == "varchar" || dataType == "char" || dataType == "nchar")
{
if (maxLength == -1)
formattedDataType += "(MAX)";
else if (dataType.StartsWith("n")) // tipi Unicode - la lunghezza è in byte, dobbiamo dividerla per 2
formattedDataType += $"({maxLength / 2})";
else
formattedDataType += $"({maxLength})";
}
else if (dataType == "decimal" || dataType == "numeric")
{
formattedDataType += $"({precision},{scale})";
}
return formattedDataType;
}
}
@@ -0,0 +1,17 @@
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DataConnection.Interfaces;
/// <summary>
/// Interfaccia per provider di estrazione dello schema del database
/// </summary>
public interface IDatabaseSchemaProvider
{
/// <summary>
/// Estrae lo schema del database (tabelle e colonne)
/// </summary>
/// <param name="connectionString">Stringa di connessione al database</param>
/// <returns>Struttura gerarchica delle tabelle e delle loro colonne</returns>
Task<IDictionary<string, IEnumerable<DbColumnInfo>>> GetDatabaseSchemaAsync(string connectionString);
}