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
This commit is contained in:
@@ -109,35 +109,26 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
{
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"[DEBUG] Iniziando GetDatabaseSchemaAsync - DatabaseType: {_options.DatabaseType}");
|
||||
|
||||
// Assicurarsi che il contesto sia connesso
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
Console.WriteLine($"[DEBUG] Connessione al database aperta. Connection string: {_context.Database.GetConnectionString()}");
|
||||
|
||||
// Usa la factory per ottenere il provider appropriato in base al tipo di database
|
||||
var schemaProvider = DatabaseSchemaProviderFactory.CreateProvider(_options.DatabaseType);
|
||||
Console.WriteLine($"[DEBUG] Schema provider creato: {schemaProvider.GetType().Name}");
|
||||
|
||||
// Usa il provider per ottenere lo schema
|
||||
var result = await schemaProvider.GetDatabaseSchemaAsync(_context.Database.GetConnectionString());
|
||||
Console.WriteLine($"[DEBUG] Schema ottenuto. Numero tabelle: {result?.Count ?? 0}");
|
||||
|
||||
if (result != null && result.Count > 0)
|
||||
{
|
||||
foreach (var table in result.Take(3))
|
||||
{
|
||||
Console.WriteLine($"[DEBUG] Tabella: {table.Key}, Colonne: {table.Value?.Count() ?? 0}");
|
||||
}
|
||||
}
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
var result = await schemaProvider.GetDatabaseSchemaAsync(connectionString);
|
||||
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Errore nel recupero dello schema del database: {ex.Message}");
|
||||
Console.WriteLine($"[DEBUG] Stack trace: {ex.StackTrace}");
|
||||
throw; }
|
||||
throw;
|
||||
}
|
||||
} public async Task<IEnumerable<Dictionary<string, object>>> GetAllRecordsAsync(string tableName)
|
||||
{
|
||||
try
|
||||
@@ -146,7 +137,8 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
|
||||
// Usa la stessa connection string utilizzata per il discovery dello schema
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
Console.WriteLine($"[DEBUG] GetAllRecordsAsync - Using connection string: {connectionString?.Substring(0, Math.Min(50, connectionString?.Length ?? 0))}...");
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
// Determina il tipo di connessione in base al DatabaseType
|
||||
using var connection = CreateConnection(connectionString);
|
||||
@@ -171,7 +163,6 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
}
|
||||
|
||||
command.CommandText = $"SELECT TOP 1000 * FROM {tableReference}";
|
||||
Console.WriteLine($"[DEBUG] GetAllRecordsAsync - Query: {command.CommandText}");
|
||||
|
||||
using var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
@@ -183,13 +174,12 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
{
|
||||
var columnName = reader.GetName(i);
|
||||
var value = reader.IsDBNull(i) ? null : reader.GetValue(i);
|
||||
record[columnName] = value;
|
||||
record[columnName] = value!;
|
||||
}
|
||||
|
||||
records.Add(record);
|
||||
}
|
||||
|
||||
Console.WriteLine($"[DEBUG] GetAllRecordsAsync - Tabella: {tableName}, Record ottenuti: {records.Count}");
|
||||
return records;
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -199,6 +189,114 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
}
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetAvailableDatabasesAsync()
|
||||
{
|
||||
try
|
||||
{
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
// Crea una connessione al server (senza specificare il database)
|
||||
var serverConnectionString = GetServerConnectionString(connectionString);
|
||||
|
||||
using var connection = CreateConnection(serverConnectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
using var command = connection.CreateCommand();
|
||||
|
||||
// Query per ottenere i database disponibili (esclude quelli di sistema)
|
||||
command.CommandText = @"
|
||||
SELECT name
|
||||
FROM sys.databases
|
||||
WHERE state_desc = 'ONLINE'
|
||||
AND name NOT IN ('master', 'tempdb', 'model', 'msdb', 'distribution')
|
||||
ORDER BY name";
|
||||
|
||||
var databases = new List<string>();
|
||||
using var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
databases.Add(reader.GetString(0));
|
||||
}
|
||||
|
||||
return databases;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Errore nell'ottenere la lista dei database: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public async Task ChangeDatabaseAsync(string databaseName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var currentConnectionString = _context.Database.GetConnectionString();
|
||||
if (currentConnectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
// Crea una nuova connection string con il database specificato
|
||||
var newConnectionString = UpdateConnectionStringDatabase(currentConnectionString, databaseName);
|
||||
|
||||
// Ricrea il contesto con la nuova connection string
|
||||
var optionsBuilder = new DbContextOptionsBuilder<ExistingDatabaseContext>();
|
||||
|
||||
switch (_options.DatabaseType)
|
||||
{
|
||||
case Enums.DatabaseType.SqlServer:
|
||||
optionsBuilder.UseSqlServer(newConnectionString, options =>
|
||||
{
|
||||
if (_options.CommandTimeout > 0)
|
||||
options.CommandTimeout(_options.CommandTimeout);
|
||||
});
|
||||
break;
|
||||
default:
|
||||
throw new NotSupportedException($"Database type {_options.DatabaseType} is not supported");
|
||||
}
|
||||
|
||||
// Disponi il vecchio contesto e crea quello nuovo
|
||||
_context.Dispose();
|
||||
_context = new ExistingDatabaseContext(
|
||||
optionsBuilder.Options,
|
||||
_options.ModelConfigurator,
|
||||
_options.EnableAutoDiscovery,
|
||||
_options.EntityAssembly,
|
||||
_options.EntityNamespace,
|
||||
_options.NamingStrategy);
|
||||
|
||||
// Testa la connessione al nuovo database
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Errore nel cambio database a '{databaseName}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Estrae la connection string del server (senza database specifico) da una connection string completa
|
||||
/// </summary>
|
||||
private string GetServerConnectionString(string connectionString)
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
builder.InitialCatalog = ""; // Rimuove il database specifico
|
||||
return builder.ConnectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Aggiorna la connection string con un nuovo database
|
||||
/// </summary>
|
||||
private string UpdateConnectionStringDatabase(string connectionString, string databaseName)
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
builder.InitialCatalog = databaseName;
|
||||
return builder.ConnectionString;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Crea una connessione database appropriata in base al tipo di database
|
||||
/// </summary>
|
||||
@@ -222,4 +320,58 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
{
|
||||
_context?.Dispose();
|
||||
}
|
||||
|
||||
public async Task<string?> GetPrimaryKeyFieldAsync(string tableName)
|
||||
{
|
||||
try
|
||||
{
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
using var connection = CreateConnection(connectionString);
|
||||
await connection.OpenAsync();
|
||||
|
||||
using var command = connection.CreateCommand();
|
||||
|
||||
// Query per ottenere la Primary Key della tabella
|
||||
// Gestisce anche tabelle con schema (es. "dbo.TableName")
|
||||
string schemaName = "dbo"; // Default schema
|
||||
string tableNameOnly = tableName;
|
||||
|
||||
if (tableName.Contains('.'))
|
||||
{
|
||||
var parts = tableName.Split('.');
|
||||
schemaName = parts[0];
|
||||
tableNameOnly = parts[1];
|
||||
}
|
||||
|
||||
command.CommandText = @"
|
||||
SELECT COLUMN_NAME
|
||||
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE
|
||||
WHERE OBJECTPROPERTY(OBJECT_ID(CONSTRAINT_SCHEMA + '.' + QUOTENAME(CONSTRAINT_NAME)), 'IsPrimaryKey') = 1
|
||||
AND TABLE_SCHEMA = @schemaName
|
||||
AND TABLE_NAME = @tableName
|
||||
ORDER BY ORDINAL_POSITION";
|
||||
|
||||
// Usa parametri per evitare SQL injection
|
||||
var schemaParam = command.CreateParameter();
|
||||
schemaParam.ParameterName = "@schemaName";
|
||||
schemaParam.Value = schemaName;
|
||||
command.Parameters.Add(schemaParam);
|
||||
|
||||
var tableParam = command.CreateParameter();
|
||||
tableParam.ParameterName = "@tableName";
|
||||
tableParam.Value = tableNameOnly;
|
||||
command.Parameters.Add(tableParam);
|
||||
|
||||
var result = await command.ExecuteScalarAsync();
|
||||
return result?.ToString();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Errore nel recupero della Primary Key per la tabella {tableName}: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,13 +17,24 @@ public class SqlServerSchemaProvider : IDatabaseSchemaProvider
|
||||
|
||||
try
|
||||
{
|
||||
Console.WriteLine($"[DEBUG] SqlServerSchemaProvider - Connection string: {connectionString?.Substring(0, Math.Min(50, connectionString?.Length ?? 0))}...");
|
||||
|
||||
using (var connection = new SqlConnection(connectionString))
|
||||
{
|
||||
await connection.OpenAsync();
|
||||
Console.WriteLine($"[DEBUG] SqlServerSchemaProvider - Connessione aperta");
|
||||
|
||||
// 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
|
||||
@@ -71,8 +82,8 @@ public class SqlServerSchemaProvider : IDatabaseSchemaProvider
|
||||
|
||||
using (var reader = await command.ExecuteReaderAsync())
|
||||
{
|
||||
string currentTable = null;
|
||||
List<DbColumnInfo> columns = null;
|
||||
string? currentTable = null;
|
||||
List<DbColumnInfo>? columns = null;
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
@@ -117,12 +128,6 @@ public class SqlServerSchemaProvider : IDatabaseSchemaProvider
|
||||
{
|
||||
result[currentTable] = columns;
|
||||
}
|
||||
|
||||
Console.WriteLine($"[DEBUG] SqlServerSchemaProvider - Query completata. Trovate {result.Count} tabelle");
|
||||
foreach (var table in result.Take(3))
|
||||
{
|
||||
Console.WriteLine($"[DEBUG] SqlServerSchemaProvider - Tabella: {table.Key}, Colonne: {table.Value?.Count() ?? 0}");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user