diff --git a/Data_Coupler/Pages/DataCoupler.razor b/Data_Coupler/Pages/DataCoupler.razor index 330035a..bf4b7cc 100644 --- a/Data_Coupler/Pages/DataCoupler.razor +++ b/Data_Coupler/Pages/DataCoupler.razor @@ -14,11 +14,6 @@ @using Microsoft.AspNetCore.Components @using Microsoft.AspNetCore.Components.Web @using Components -@inject IDataConnectionCredentialService CredentialService -@inject IDataConnectionFactory ConnectionFactory -@inject IJSRuntime JSRuntime -@inject ILogger Logger -@inject CredentialManager.Services.IDataCouplerProfileService ProfileService Data Coupler @@ -168,12 +163,22 @@ } - @if (!string.IsNullOrEmpty(queryValidationMessage) && !isQueryValid) + @if (!string.IsNullOrEmpty(queryValidationMessage)) { - + @if (isQueryValid) + { + + } + else + { + + } } diff --git a/Data_Coupler/Pages/DataCoupler.razor.cs b/Data_Coupler/Pages/DataCoupler.razor.cs index e4fa7c7..a7884b7 100644 --- a/Data_Coupler/Pages/DataCoupler.razor.cs +++ b/Data_Coupler/Pages/DataCoupler.razor.cs @@ -6,16 +6,26 @@ using CredentialManager.Services; using DataConnection.Interfaces; using DataConnection.REST.Interfaces; using DataConnection.REST.Models; +using DataConnection.CredentialManagement.Interfaces; using ExcelDataReader; using Microsoft.AspNetCore.Components; using Microsoft.AspNetCore.Components.Forms; using Microsoft.JSInterop; +using Microsoft.Extensions.Logging; +using Data_Coupler.Services; namespace Data_Coupler.Pages; -public partial class DataCoupler +public partial class DataCoupler : ComponentBase { -// Classe per i risultati del trasferimento + // Proprietà iniettate + [Inject] public IDataConnectionCredentialService CredentialService { get; set; } = default!; + [Inject] public IDataConnectionFactory ConnectionFactory { get; set; } = default!; + [Inject] public IJSRuntime JSRuntime { get; set; } = default!; + [Inject] public ILogger Logger { get; set; } = default!; + [Inject] public IDataCouplerProfileService ProfileService { get; set; } = default!; + + // Classe per i risultati del trasferimento public class TransferResult { public int RecordNumber { get; set; } @@ -2085,8 +2095,22 @@ public partial class DataCoupler var cleanQuery = CleanQuery(customQuery); + // Trova la credenziale per determinare il tipo di database + var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential); + if (credential == null) + { + isQueryValid = false; + queryValidationMessage = "Credenziale database non trovata"; + return; + } + + // Crea una query di test con sintassi appropriata per il tipo di database + var testQuery = CreateLimitedQuery(cleanQuery, credential.DatabaseType, 1); + + Logger.LogInformation("Validando query: {Query}", testQuery); + // Prova a eseguire la query per validarla - var testResults = await currentDatabaseManager.ExecuteRawQueryAsync($"{cleanQuery} LIMIT 1"); + var testResults = await currentDatabaseManager.ExecuteRawQueryAsync(testQuery); if (testResults != null && testResults.Any()) { @@ -2133,8 +2157,18 @@ public partial class DataCoupler { var cleanQuery = CleanQuery(customQuery); - // Aggiungi LIMIT per l'anteprima - var previewQuery = $"{cleanQuery} LIMIT 10"; + // Trova la credenziale per determinare il tipo di database + var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential); + if (credential == null) + { + queryValidationMessage = "Credenziale database non trovata"; + return; + } + + // Crea una query di anteprima con sintassi appropriata per il tipo di database + var previewQuery = CreateLimitedQuery(cleanQuery, credential.DatabaseType, 10); + + Logger.LogInformation("Caricando anteprima con query: {Query}", previewQuery); var previewResults = await currentDatabaseManager.ExecuteRawQueryAsync(previewQuery); queryPreviewData = previewResults.ToList(); @@ -2154,6 +2188,24 @@ public partial class DataCoupler } } + /// + /// Crea una query limitata in base al tipo di database + /// + private string CreateLimitedQuery(string baseQuery, DatabaseType databaseType, int limit) + { + return databaseType switch + { + DatabaseType.SqlServer => $"SELECT TOP {limit} * FROM ({baseQuery}) AS subquery", + DatabaseType.Oracle => $"SELECT * FROM ({baseQuery}) WHERE ROWNUM <= {limit}", + DatabaseType.MySql => $"{baseQuery} LIMIT {limit}", + DatabaseType.PostgreSql => $"{baseQuery} LIMIT {limit}", + DatabaseType.Sqlite => $"{baseQuery} LIMIT {limit}", + DatabaseType.DB2 => $"SELECT * FROM ({baseQuery}) FETCH FIRST {limit} ROWS ONLY", + DatabaseType.SapHana => $"{baseQuery} LIMIT {limit}", + _ => $"{baseQuery} LIMIT {limit}" // Default a LIMIT per database non riconosciuti + }; + } + /// /// Nasconde l'anteprima della query /// @@ -2215,24 +2267,177 @@ public partial class DataCoupler try { - Logger.LogInformation("Database selezionato: {Database}. Riconnessione al database...", selectedDatabase); + Logger.LogInformation("Database selezionato: {Database}. Tentativo di connessione diretta...", selectedDatabase); - // Riconnetti al database utilizzando il database selezionato come schema - await ConnectToDatabaseWithSchema(selectedDatabase); - - if (isDatabaseConnected) + // Trova la credenziale corrente + var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential); + if (credential == null) { - Logger.LogInformation("Connessione completata con successo usando il database {Database}", selectedDatabase); - databaseErrorMessage = ""; // Pulisci eventuali errori precedenti + databaseErrorMessage = "Credenziale database non trovata"; + return; + } + + isConnectingDatabase = true; + databaseErrorMessage = ""; + + // Disponi il manager precedente + currentDatabaseManager?.Dispose(); + currentDatabaseManager = null; + + // Per ora, proviamo a usare il database manager esistente e cercare le tabelle con query dirette + // TODO: In futuro, modificare il ConnectionFactory per supportare database specifici + currentDatabaseManager = await ConnectionFactory.CreateDatabaseManagerAsync(selectedDatabaseCredential); + + // Prova a ottenere le tabelle del database specifico usando query dirette + var tablesQuery = credential.DatabaseType switch + { + DatabaseType.SqlServer => $"SELECT TABLE_NAME FROM {selectedDatabase}.INFORMATION_SCHEMA.TABLES WHERE TABLE_TYPE = 'BASE TABLE'", + DatabaseType.MySql => $"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{selectedDatabase}' AND TABLE_TYPE = 'BASE TABLE'", + DatabaseType.PostgreSql => $"SELECT tablename as TABLE_NAME FROM pg_tables WHERE schemaname = '{selectedDatabase}'", + DatabaseType.Oracle => $"SELECT TABLE_NAME FROM ALL_TABLES WHERE OWNER = '{selectedDatabase.ToUpper()}'", + _ => $"SELECT TABLE_NAME FROM INFORMATION_SCHEMA.TABLES WHERE TABLE_SCHEMA = '{selectedDatabase}' AND TABLE_TYPE = 'BASE TABLE'" + }; + + Logger.LogInformation("Eseguendo query per tabelle del database {Database}: {Query}", selectedDatabase, tablesQuery); + + var tableResults = await currentDatabaseManager.ExecuteRawQueryAsync(tablesQuery); + + if (tableResults != null && tableResults.Any()) + { + // Converte i risultati in un dizionario di tabelle + databaseTables.Clear(); + + foreach (var row in tableResults) + { + var tableName = row.Values.FirstOrDefault()?.ToString(); + if (!string.IsNullOrEmpty(tableName)) + { + // Per ogni tabella, prova a ottenere le colonne + try + { + var fullTableName = credential.DatabaseType == DatabaseType.SqlServer + ? $"{selectedDatabase}.dbo.{tableName}" + : $"{selectedDatabase}.{tableName}"; + + var columns = await GetTableColumns(fullTableName, selectedDatabase, tableName); + databaseTables[fullTableName] = columns; + } + catch (Exception colEx) + { + Logger.LogWarning(colEx, "Impossibile ottenere colonne per la tabella {TableName}", tableName); + // Aggiungi la tabella anche senza colonne specifiche + databaseTables[tableName] = new List(); + } + } + } + + isDatabaseConnected = true; + Logger.LogInformation("Connessione completata con successo. Database: {Database}, Tabelle: {TableCount}", + selectedDatabase, databaseTables.Count); + databaseErrorMessage = ""; + } + else + { + databaseErrorMessage = $"Nessuna tabella trovata nel database {selectedDatabase}"; + Logger.LogWarning("Nessuna tabella trovata nel database {Database}", selectedDatabase); } } catch (Exception ex) { - Logger.LogError(ex, "Errore nella connessione con il database selezionato"); + Logger.LogError(ex, "Errore nella connessione con il database selezionato: {Database}", selectedDatabase); databaseErrorMessage = $"Errore nella connessione con database {selectedDatabase}: {ex.Message}"; + isDatabaseConnected = false; + } + finally + { + isConnectingDatabase = false; + StateHasChanged(); + } + } + + /// + /// Ottiene le colonne di una tabella specifica + /// + private async Task> GetTableColumns(string fullTableName, string databaseName, string tableName) + { + var columns = new List(); + + try + { + var credential = databaseCredentials.FirstOrDefault(c => c.Name == selectedDatabaseCredential); + if (credential == null) return columns; + + var columnsQuery = credential.DatabaseType switch + { + DatabaseType.SqlServer => $@" + SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, + CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NOT NULL THEN CHARACTER_MAXIMUM_LENGTH + ELSE NUMERIC_PRECISION END as MAX_LENGTH + FROM {databaseName}.INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_NAME = '{tableName}' + ORDER BY ORDINAL_POSITION", + + DatabaseType.MySql => $@" + SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, + CASE WHEN CHARACTER_MAXIMUM_LENGTH IS NOT NULL THEN CHARACTER_MAXIMUM_LENGTH + ELSE NUMERIC_PRECISION END as MAX_LENGTH + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = '{databaseName}' AND TABLE_NAME = '{tableName}' + ORDER BY ORDINAL_POSITION", + + DatabaseType.PostgreSql => $@" + SELECT column_name as COLUMN_NAME, data_type as DATA_TYPE, + is_nullable as IS_NULLABLE, character_maximum_length as MAX_LENGTH + FROM information_schema.columns + WHERE table_schema = '{databaseName}' AND table_name = '{tableName}' + ORDER BY ordinal_position", + + DatabaseType.Oracle => $@" + SELECT COLUMN_NAME, DATA_TYPE, NULLABLE as IS_NULLABLE, DATA_LENGTH as MAX_LENGTH + FROM ALL_TAB_COLUMNS + WHERE OWNER = '{databaseName.ToUpper()}' AND TABLE_NAME = '{tableName.ToUpper()}' + ORDER BY COLUMN_ID", + + _ => $@" + SELECT COLUMN_NAME, DATA_TYPE, IS_NULLABLE, CHARACTER_MAXIMUM_LENGTH as MAX_LENGTH + FROM INFORMATION_SCHEMA.COLUMNS + WHERE TABLE_SCHEMA = '{databaseName}' AND TABLE_NAME = '{tableName}' + ORDER BY ORDINAL_POSITION" + }; + + var columnResults = await currentDatabaseManager!.ExecuteRawQueryAsync(columnsQuery); + + if (columnResults != null) + { + foreach (var row in columnResults) + { + var columnName = row.GetValueOrDefault("COLUMN_NAME")?.ToString() ?? ""; + var dataType = row.GetValueOrDefault("DATA_TYPE")?.ToString() ?? ""; + var isNullable = row.GetValueOrDefault("IS_NULLABLE")?.ToString()?.ToUpper() == "YES"; + + if (int.TryParse(row.GetValueOrDefault("MAX_LENGTH")?.ToString(), out int maxLength)) + { + // Usa maxLength se necessario + } + + if (!string.IsNullOrEmpty(columnName)) + { + columns.Add(new DbColumnInfo + { + Name = columnName, + DataType = dataType, + IsNullable = isNullable + }); + } + } + } + } + catch (Exception ex) + { + Logger.LogError(ex, "Errore nell'ottenere le colonne per la tabella {TableName}", fullTableName); } - StateHasChanged(); + return columns; } ///