Files
Data-Coupler/DataConnection/DB/EF/SchemaProviders/SqlServerSchemaProvider.cs
T
Alessio 51c61eabf7 feat: Implementa gestione intelligente della chiave sorgente con rilevamento PK
- Aggiunge rilevamento automatico Primary Key per connessioni database
- Rimuove completamente il fallback automatico per lato sorgente
- Implementa selezione manuale obbligatoria per file e sorgenti non-DB
- Migliora UI con suggerimenti intelligenti e feedback visivo
- Aggiunge validazione multi-livello (UI, pre-transfer, runtime)
- Introduce metodo GetPrimaryKeyFieldAsync in IDatabaseManager
- Modifica GenerateSourceKey per richiedere sempre campo specifico
- Implementa controllo IsTransferButtonEnabled per validazione form

Breaking changes:
- La generazione automatica delle chiavi sorgente è stata rimossa
- Il campo chiave sorgente è ora obbligatorio quando si usa il sistema associazioni

Fixes: Risolve problema di discovery schema vuoto con selezione database
2025-06-28 02:05:59 +02:00

164 lines
7.5 KiB
C#

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();
// Prima verifichiamo se ci sono tabelle utente con una query semplice
string testSql = "SELECT COUNT(*) FROM sys.tables WHERE is_ms_shipped = 0";
using (var testCommand = new SqlCommand(testSql, connection))
{
var scalarResult = await testCommand.ExecuteScalarAsync();
var tableCount = scalarResult != null ? (int)scalarResult : 0;
if (tableCount == 0)
{
return new Dictionary<string, IEnumerable<DbColumnInfo>>(); // Restituisce dizionario vuoto
}
}
// Se ci sono tabelle, procediamo con la query completa
// 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;
}
}