Prima implementazione del servizio di connessione e scoperta del database
This commit is contained in:
@@ -0,0 +1,20 @@
|
|||||||
|
<Project Sdk="Microsoft.NET.Sdk">
|
||||||
|
|
||||||
|
<PropertyGroup>
|
||||||
|
<TargetFramework>net9.0</TargetFramework>
|
||||||
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
|
<Nullable>enable</Nullable>
|
||||||
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<Folder Include="Models\" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<PackageReference Include="IBM.EntityFrameworkCore" Version="6.0.0.300" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="9.0.3" />
|
||||||
|
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="9.0.3" />
|
||||||
|
<PackageReference Include="System.Data.Odbc" Version="9.0.3" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
|
</Project>
|
||||||
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Data.SqlClient;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DataConnection.Interfaces;
|
||||||
|
using Microsoft.Data.SqlClient;
|
||||||
|
|
||||||
|
namespace DataConnection.EF.DatabaseDiscovery;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementazione di IDatabaseDiscovery per SQL Server
|
||||||
|
/// </summary>
|
||||||
|
public class SqlServerDatabaseDiscovery : IDatabaseDiscovery
|
||||||
|
{
|
||||||
|
private static readonly List<string> _systemDatabases = new List<string>
|
||||||
|
{
|
||||||
|
"master", "tempdb", "model", "msdb", "distribution"
|
||||||
|
};
|
||||||
|
|
||||||
|
public async Task<List<string>> GetAvailableDatabasesAsync(string serverConnectionString, bool excludeSystemDatabases = true)
|
||||||
|
{
|
||||||
|
List<string> databases = new List<string>();
|
||||||
|
|
||||||
|
using (SqlConnection connection = new SqlConnection(serverConnectionString))
|
||||||
|
{
|
||||||
|
await connection.OpenAsync();
|
||||||
|
|
||||||
|
// Query per ottenere tutti i database sul server
|
||||||
|
string query = @"
|
||||||
|
SELECT name
|
||||||
|
FROM sys.databases
|
||||||
|
WHERE state_desc = 'ONLINE'";
|
||||||
|
|
||||||
|
if (excludeSystemDatabases)
|
||||||
|
{
|
||||||
|
query += " AND name NOT IN ('master', 'tempdb', 'model', 'msdb', 'distribution')";
|
||||||
|
}
|
||||||
|
|
||||||
|
query += " ORDER BY name";
|
||||||
|
|
||||||
|
using (SqlCommand command = new SqlCommand(query, connection))
|
||||||
|
{
|
||||||
|
using (SqlDataReader reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
databases.Add(reader.GetString(0));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return databases;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, DatabaseInfo>> GetDatabasesInfoAsync(string serverConnectionString, bool excludeSystemDatabases = true)
|
||||||
|
{
|
||||||
|
Dictionary<string, DatabaseInfo> databasesInfo = new Dictionary<string, DatabaseInfo>();
|
||||||
|
|
||||||
|
using (SqlConnection connection = new SqlConnection(serverConnectionString))
|
||||||
|
{
|
||||||
|
await connection.OpenAsync();
|
||||||
|
|
||||||
|
// Query per ottenere informazioni dettagliate sui database
|
||||||
|
string query = @"
|
||||||
|
SELECT
|
||||||
|
d.name,
|
||||||
|
CAST((SELECT SUM(CAST(size AS BIGINT)) * 8.0 / 1024 FROM sys.master_files WHERE database_id = d.database_id) AS DECIMAL(18,2)) AS size_mb,
|
||||||
|
d.create_date,
|
||||||
|
d.state_desc,
|
||||||
|
SUSER_SNAME(d.owner_sid) AS owner,
|
||||||
|
CASE WHEN d.name IN ('master', 'tempdb', 'model', 'msdb', 'distribution') THEN 1 ELSE 0 END AS is_system_db
|
||||||
|
FROM sys.databases d
|
||||||
|
WHERE state_desc = 'ONLINE'";
|
||||||
|
|
||||||
|
if (excludeSystemDatabases)
|
||||||
|
{
|
||||||
|
query += " AND d.name NOT IN ('master', 'tempdb', 'model', 'msdb', 'distribution')";
|
||||||
|
}
|
||||||
|
|
||||||
|
query += " ORDER BY d.name";
|
||||||
|
|
||||||
|
using (SqlCommand command = new SqlCommand(query, connection))
|
||||||
|
{
|
||||||
|
using (SqlDataReader reader = await command.ExecuteReaderAsync())
|
||||||
|
{
|
||||||
|
while (await reader.ReadAsync())
|
||||||
|
{
|
||||||
|
string dbName = reader.GetString(0);
|
||||||
|
|
||||||
|
databasesInfo[dbName] = new DatabaseInfo
|
||||||
|
{
|
||||||
|
Name = dbName,
|
||||||
|
SizeMB = reader.IsDBNull(1) ? 0 : Convert.ToDouble(reader.GetDecimal(1)),
|
||||||
|
CreationDate = reader.GetDateTime(2),
|
||||||
|
Status = reader.GetString(3),
|
||||||
|
Owner = reader.IsDBNull(4) ? null : reader.GetString(4),
|
||||||
|
IsSystemDatabase = reader.GetInt32(5) == 1
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return databasesInfo;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,116 @@
|
|||||||
|
using System;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using DataConnection.Interfaces;
|
||||||
|
using DataConnection.EF.DatabaseDiscovery;
|
||||||
|
using DataConnection.Enums;
|
||||||
|
|
||||||
|
namespace DataConnection.EF;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Opzioni per la configurazione del manager di database esistenti
|
||||||
|
/// </summary>
|
||||||
|
public class DbManagerOptions
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Configuratore del DbContext
|
||||||
|
/// </summary>
|
||||||
|
public Action<DbContextOptionsBuilder> DbContextConfigurator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configuratore del modello del database
|
||||||
|
/// </summary>
|
||||||
|
public Action<ModelBuilder> ModelConfigurator { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Flag che indica se la scoperta automatica delle entità è abilitata
|
||||||
|
/// </summary>
|
||||||
|
public bool EnableAutoDiscovery { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Assembly da cui caricare automaticamente le entità (se EnableAutoDiscovery = true)
|
||||||
|
/// </summary>
|
||||||
|
public System.Reflection.Assembly EntityAssembly { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Namespace in cui cercare le entità (se EnableAutoDiscovery = true)
|
||||||
|
/// </summary>
|
||||||
|
public string EntityNamespace { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout per le operazioni del database (in secondi)
|
||||||
|
/// </summary>
|
||||||
|
public int CommandTimeout { get; set; } = 30;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Strategia di mappatura dei nomi (CamelCase, PascalCase, SnakeCase)
|
||||||
|
/// </summary>
|
||||||
|
public NamingStrategy NamingStrategy { get; set; } = NamingStrategy.Default;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Servizio per la scoperta dei database disponibili sul server
|
||||||
|
/// </summary>
|
||||||
|
public IDatabaseDiscovery DatabaseDiscoveryService { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stringa di connessione a livello di server (senza specificare il database)
|
||||||
|
/// </summary>
|
||||||
|
public string ServerConnectionString { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Nome del database a cui connettersi
|
||||||
|
/// </summary>
|
||||||
|
public string DatabaseName { get; set; }
|
||||||
|
|
||||||
|
/// <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)
|
||||||
|
{
|
||||||
|
switch (databaseType)
|
||||||
|
{
|
||||||
|
case DatabaseType.SqlServer:
|
||||||
|
DatabaseDiscoveryService = new SqlServerDatabaseDiscovery();
|
||||||
|
break;
|
||||||
|
// case DatabaseType.MySql:
|
||||||
|
// DatabaseDiscoveryService = new MySqlDatabaseDiscovery();
|
||||||
|
// break;
|
||||||
|
// Altri tipi di database possono essere aggiunti qui
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException($"Tipo di database non supportato: {databaseType}");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Costruisce una stringa di connessione completa includendo il database selezionato
|
||||||
|
/// </summary>
|
||||||
|
public string BuildFullConnectionString()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(ServerConnectionString))
|
||||||
|
{
|
||||||
|
throw new InvalidOperationException("La stringa di connessione al server non è stata specificata");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(DatabaseName))
|
||||||
|
{
|
||||||
|
return ServerConnectionString;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per SQL Server
|
||||||
|
if (ServerConnectionString.Contains("Initial Catalog=") || ServerConnectionString.Contains("Database="))
|
||||||
|
{
|
||||||
|
// Sostituisci il database esistente
|
||||||
|
var modifiedString = System.Text.RegularExpressions.Regex.Replace(
|
||||||
|
ServerConnectionString,
|
||||||
|
@"(Initial Catalog|Database)=([^;]*)",
|
||||||
|
$"$1={DatabaseName}");
|
||||||
|
|
||||||
|
return modifiedString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Aggiungi il database
|
||||||
|
return ServerConnectionString + $";Database={DatabaseName}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,159 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Data;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DataConnection.Interfaces;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||||
|
using Microsoft.EntityFrameworkCore.Metadata;
|
||||||
|
|
||||||
|
namespace DataConnection.EF;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Implementazione di IExistingDatabaseManager basata su Entity Framework Core
|
||||||
|
/// </summary>
|
||||||
|
public class EFCoreDatabaseManager : IDatabaseManager
|
||||||
|
{
|
||||||
|
private readonly DbManagerOptions _options;
|
||||||
|
private ExistingDatabaseContext _context;
|
||||||
|
|
||||||
|
public EFCoreDatabaseManager(DbManagerOptions options)
|
||||||
|
{
|
||||||
|
_options = options ?? throw new ArgumentNullException(nameof(options));
|
||||||
|
InitializeContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void InitializeContext()
|
||||||
|
{
|
||||||
|
var optionsBuilder = new DbContextOptionsBuilder<ExistingDatabaseContext>();
|
||||||
|
_options.DbContextConfigurator(optionsBuilder);
|
||||||
|
|
||||||
|
_context = new ExistingDatabaseContext(
|
||||||
|
optionsBuilder.Options,
|
||||||
|
_options.ModelConfigurator,
|
||||||
|
_options.EnableAutoDiscovery,
|
||||||
|
_options.EntityAssembly,
|
||||||
|
_options.EntityNamespace,
|
||||||
|
_options.NamingStrategy);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> TestConnectionAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
return await _context.Database.CanConnectAsync();
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<T>> GetAsync<T>(
|
||||||
|
Expression<Func<T, bool>> filter = null,
|
||||||
|
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
|
||||||
|
string includeProperties = "",
|
||||||
|
int? skip = null,
|
||||||
|
int? take = null) where T : class
|
||||||
|
{
|
||||||
|
IQueryable<T> query = _context.Set<T>();
|
||||||
|
|
||||||
|
if (filter != null)
|
||||||
|
{
|
||||||
|
query = query.Where(filter);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var includeProperty in includeProperties.Split
|
||||||
|
(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries))
|
||||||
|
{
|
||||||
|
query = query.Include(includeProperty);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (orderBy != null)
|
||||||
|
{
|
||||||
|
query = orderBy(query);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (skip.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Skip(skip.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (take.HasValue)
|
||||||
|
{
|
||||||
|
query = query.Take(take.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
return await query.ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<T> GetByIdAsync<T>(object id) where T : class
|
||||||
|
{
|
||||||
|
return await _context.Set<T>().FindAsync(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<IEnumerable<T>> ExecuteQueryAsync<T>(string sql, params object[] parameters) where T : class
|
||||||
|
{
|
||||||
|
return await _context.Set<T>().FromSqlRaw(sql, parameters).ToListAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<int> ExecuteCommandAsync(string sql, params object[] parameters)
|
||||||
|
{
|
||||||
|
return await _context.Database.ExecuteSqlRawAsync(sql, parameters);
|
||||||
|
}
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var tableName = entityType.GetTableName();
|
||||||
|
var schemaName = entityType.GetSchema();
|
||||||
|
var fullTableName = string.IsNullOrEmpty(schemaName) ? tableName : $"{schemaName}.{tableName}";
|
||||||
|
|
||||||
|
var columns = new List<DbColumnInfo>();
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Dispose()
|
||||||
|
{
|
||||||
|
_context?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,124 @@
|
|||||||
|
using System;
|
||||||
|
using System.Linq;
|
||||||
|
using Microsoft.EntityFrameworkCore;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Reflection;
|
||||||
|
using DataConnection.Enums;
|
||||||
|
|
||||||
|
namespace DataConnection.EF;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// DbContext per gestione database esistenti
|
||||||
|
/// </summary>
|
||||||
|
public class ExistingDatabaseContext : DbContext
|
||||||
|
{
|
||||||
|
private readonly Action<ModelBuilder> _modelConfigurator;
|
||||||
|
private readonly bool _enableAutoDiscovery;
|
||||||
|
private readonly Assembly _entityAssembly;
|
||||||
|
private readonly string _entityNamespace;
|
||||||
|
private readonly NamingStrategy _namingStrategy;
|
||||||
|
|
||||||
|
public ExistingDatabaseContext(
|
||||||
|
DbContextOptions options,
|
||||||
|
Action<ModelBuilder> modelConfigurator = null,
|
||||||
|
bool enableAutoDiscovery = false,
|
||||||
|
Assembly entityAssembly = null,
|
||||||
|
string entityNamespace = null,
|
||||||
|
NamingStrategy namingStrategy = NamingStrategy.Default)
|
||||||
|
: base(options)
|
||||||
|
{
|
||||||
|
_modelConfigurator = modelConfigurator;
|
||||||
|
_enableAutoDiscovery = enableAutoDiscovery;
|
||||||
|
_entityAssembly = entityAssembly;
|
||||||
|
_entityNamespace = entityNamespace;
|
||||||
|
_namingStrategy = namingStrategy;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
base.OnModelCreating(modelBuilder);
|
||||||
|
|
||||||
|
// Applica la strategia di mappatura dei nomi
|
||||||
|
ApplyNamingStrategy(modelBuilder);
|
||||||
|
|
||||||
|
// Applica la configurazione personalizzata se fornita
|
||||||
|
_modelConfigurator?.Invoke(modelBuilder);
|
||||||
|
|
||||||
|
// Scoperta automatica delle entità
|
||||||
|
if (_enableAutoDiscovery && _entityAssembly != null)
|
||||||
|
{
|
||||||
|
DiscoverEntities(modelBuilder);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ApplyNamingStrategy(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
switch (_namingStrategy)
|
||||||
|
{
|
||||||
|
case NamingStrategy.CamelCase:
|
||||||
|
foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
||||||
|
{
|
||||||
|
// Converti il nome della tabella in camelCase
|
||||||
|
string tableName = entity.GetTableName();
|
||||||
|
if (!string.IsNullOrEmpty(tableName) && char.IsUpper(tableName[0]))
|
||||||
|
entity.SetTableName(char.ToLower(tableName[0]) + tableName.Substring(1));
|
||||||
|
|
||||||
|
// Converti i nomi delle proprietà in camelCase
|
||||||
|
foreach (var property in entity.GetProperties())
|
||||||
|
{
|
||||||
|
string propertyName = property.GetColumnName();
|
||||||
|
if (!string.IsNullOrEmpty(propertyName) && char.IsUpper(propertyName[0]))
|
||||||
|
property.SetColumnName(char.ToLower(propertyName[0]) + propertyName.Substring(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case NamingStrategy.SnakeCase:
|
||||||
|
foreach (var entity in modelBuilder.Model.GetEntityTypes())
|
||||||
|
{
|
||||||
|
// Converti il nome della tabella in snake_case
|
||||||
|
string tableName = entity.GetTableName();
|
||||||
|
if (!string.IsNullOrEmpty(tableName))
|
||||||
|
entity.SetTableName(ConvertToSnakeCase(tableName));
|
||||||
|
|
||||||
|
// Converti i nomi delle proprietà in snake_case
|
||||||
|
foreach (var property in entity.GetProperties())
|
||||||
|
{
|
||||||
|
string propertyName = property.GetColumnName();
|
||||||
|
if (!string.IsNullOrEmpty(propertyName))
|
||||||
|
property.SetColumnName(ConvertToSnakeCase(propertyName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private string ConvertToSnakeCase(string input)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(input))
|
||||||
|
return input;
|
||||||
|
|
||||||
|
return string.Concat(input.Select((x, i) => i > 0 && char.IsUpper(x) ? "_" + x.ToString() : x.ToString())).ToLower();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DiscoverEntities(ModelBuilder modelBuilder)
|
||||||
|
{
|
||||||
|
// Trova tutte le classi nel namespace specificato che potrebbero essere entità
|
||||||
|
var entityTypes = _entityAssembly.GetTypes()
|
||||||
|
.Where(t => !string.IsNullOrEmpty(_entityNamespace)
|
||||||
|
? t.Namespace == _entityNamespace
|
||||||
|
: true)
|
||||||
|
.Where(t => t.IsClass && !t.IsAbstract && t.GetConstructor(Type.EmptyTypes) != null)
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
// Metodo generico per aggiungere entità al modello
|
||||||
|
var entityMethod = typeof(ModelBuilder).GetMethod("Entity", new Type[0]);
|
||||||
|
|
||||||
|
foreach (var entityType in entityTypes)
|
||||||
|
{
|
||||||
|
// Usa reflection per chiamare il metodo generico Entity<T>()
|
||||||
|
var genericMethod = entityMethod.MakeGenericMethod(entityType);
|
||||||
|
genericMethod.Invoke(modelBuilder, null);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
namespace DataConnection.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tipo di database supportati
|
||||||
|
/// </summary>
|
||||||
|
public enum DatabaseType
|
||||||
|
{
|
||||||
|
SqlServer,
|
||||||
|
MySql,
|
||||||
|
PostgreSql,
|
||||||
|
Oracle,
|
||||||
|
Sqlite,
|
||||||
|
DB2,
|
||||||
|
SapHana
|
||||||
|
}
|
||||||
@@ -0,0 +1,12 @@
|
|||||||
|
namespace DataConnection.Enums;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Strategia di mappatura dei nomi delle tabelle e colonne
|
||||||
|
/// </summary>
|
||||||
|
public enum NamingStrategy
|
||||||
|
{
|
||||||
|
Default,
|
||||||
|
CamelCase,
|
||||||
|
PascalCase,
|
||||||
|
SnakeCase
|
||||||
|
}
|
||||||
@@ -0,0 +1,62 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DataConnection.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaccia per la scoperta dei database disponibili sul server
|
||||||
|
/// </summary>
|
||||||
|
public interface IDatabaseDiscovery
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene l'elenco di tutti i database disponibili sul server
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverConnectionString">Stringa di connessione al server (senza specificare il database)</param>
|
||||||
|
/// <param name="excludeSystemDatabases">Se true, esclude i database di sistema</param>
|
||||||
|
/// <returns>Lista di nomi dei database disponibili</returns>
|
||||||
|
Task<List<string>> GetAvailableDatabasesAsync(string serverConnectionString, bool excludeSystemDatabases = true);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene informazioni dettagliate sui database disponibili
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="serverConnectionString">Stringa di connessione al server</param>
|
||||||
|
/// <param name="excludeSystemDatabases">Se true, esclude i database di sistema</param>
|
||||||
|
/// <returns>Dizionario con nome database e metadati</returns>
|
||||||
|
Task<Dictionary<string, DatabaseInfo>> GetDatabasesInfoAsync(string serverConnectionString, bool excludeSystemDatabases = true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Informazioni sul database
|
||||||
|
/// </summary>
|
||||||
|
public class DatabaseInfo
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Nome del database
|
||||||
|
/// </summary>
|
||||||
|
public string Name { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Dimensione del database in MB
|
||||||
|
/// </summary>
|
||||||
|
public double SizeMB { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Data di creazione
|
||||||
|
/// </summary>
|
||||||
|
public System.DateTime CreationDate { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stato del database (online, offline, ecc.)
|
||||||
|
/// </summary>
|
||||||
|
public string Status { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Proprietario del database
|
||||||
|
/// </summary>
|
||||||
|
public string Owner { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indica se è un database di sistema
|
||||||
|
/// </summary>
|
||||||
|
public bool IsSystemDatabase { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq.Expressions;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace DataConnection.Interfaces;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Interfaccia per la gestione di database preesistenti tramite EF Core
|
||||||
|
/// </summary>
|
||||||
|
public interface IDatabaseManager : IDisposable
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Verifica la connessione al database
|
||||||
|
/// </summary>
|
||||||
|
Task<bool> TestConnectionAsync();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene entità dal database in base ai criteri specificati
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">Tipo di entità</typeparam>
|
||||||
|
/// <param name="filter">Espressione di filtro</param>
|
||||||
|
/// <param name="orderBy">Espressione di ordinamento</param>
|
||||||
|
/// <param name="includeProperties">Proprietà di navigazione da includere</param>
|
||||||
|
/// <param name="skip">Numero di elementi da saltare</param>
|
||||||
|
/// <param name="take">Numero di elementi da prendere</param>
|
||||||
|
Task<IEnumerable<T>> GetAsync<T>(
|
||||||
|
Expression<Func<T, bool>> filter = null,
|
||||||
|
Func<IQueryable<T>, IOrderedQueryable<T>> orderBy = null,
|
||||||
|
string includeProperties = "",
|
||||||
|
int? skip = null,
|
||||||
|
int? take = null) where T : class;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene un'entità singola in base alla chiave primaria
|
||||||
|
/// </summary>
|
||||||
|
Task<T> GetByIdAsync<T>(object id) where T : class;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Esegue una query SQL raw
|
||||||
|
/// </summary>
|
||||||
|
Task<IEnumerable<T>> ExecuteQueryAsync<T>(string sql, params object[] parameters) where T : class;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Esegue un comando SQL che non restituisce risultati
|
||||||
|
/// </summary>
|
||||||
|
Task<int> ExecuteCommandAsync(string sql, params object[] parameters);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Ottiene i metadati delle tabelle nel database
|
||||||
|
/// </summary>
|
||||||
|
Task<IDictionary<string, IEnumerable<DbColumnInfo>>> GetDatabaseSchemaAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Informazioni su una colonna del database
|
||||||
|
/// </summary>
|
||||||
|
public class DbColumnInfo
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public string DataType { get; set; }
|
||||||
|
public bool IsNullable { get; set; }
|
||||||
|
public bool IsPrimaryKey { get; set; }
|
||||||
|
public bool IsForeignKey { get; set; }
|
||||||
|
public string ReferencedTable { get; set; }
|
||||||
|
public string ReferencedColumn { get; set; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||||
|
<clear />
|
||||||
|
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
||||||
@@ -5,6 +5,8 @@ VisualStudioVersion = 17.0.31903.59
|
|||||||
MinimumVisualStudioVersion = 10.0.40219.1
|
MinimumVisualStudioVersion = 10.0.40219.1
|
||||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data_Coupler", "Data_Coupler\Data_Coupler.csproj", "{899D735C-16BB-497B-AE81-A0C3845FD669}"
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Data_Coupler", "Data_Coupler\Data_Coupler.csproj", "{899D735C-16BB-497B-AE81-A0C3845FD669}"
|
||||||
EndProject
|
EndProject
|
||||||
|
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DataConnection", "DataConnection\DataConnection.csproj", "{27A4090F-2D6D-49F2-B0C7-6D33E90235BA}"
|
||||||
|
EndProject
|
||||||
Global
|
Global
|
||||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||||
Debug|Any CPU = Debug|Any CPU
|
Debug|Any CPU = Debug|Any CPU
|
||||||
@@ -18,5 +20,9 @@ Global
|
|||||||
{899D735C-16BB-497B-AE81-A0C3845FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
{899D735C-16BB-497B-AE81-A0C3845FD669}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
{899D735C-16BB-497B-AE81-A0C3845FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
{899D735C-16BB-497B-AE81-A0C3845FD669}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
{899D735C-16BB-497B-AE81-A0C3845FD669}.Release|Any CPU.Build.0 = Release|Any CPU
|
{899D735C-16BB-497B-AE81-A0C3845FD669}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
|
{27A4090F-2D6D-49F2-B0C7-6D33E90235BA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||||
|
{27A4090F-2D6D-49F2-B0C7-6D33E90235BA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||||
|
{27A4090F-2D6D-49F2-B0C7-6D33E90235BA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||||
|
{27A4090F-2D6D-49F2-B0C7-6D33E90235BA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||||
EndGlobalSection
|
EndGlobalSection
|
||||||
EndGlobal
|
EndGlobal
|
||||||
|
|||||||
@@ -0,0 +1,108 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using DataConnection.Interfaces;
|
||||||
|
using DataConnection.EF;
|
||||||
|
using DataConnection.EF.DatabaseDiscovery;
|
||||||
|
using DataConnection.Enums;
|
||||||
|
|
||||||
|
namespace Data_Coupler.Data
|
||||||
|
{
|
||||||
|
public class DatabaseConnectionService
|
||||||
|
{
|
||||||
|
private readonly IDatabaseDiscovery _sqlServerDiscovery;
|
||||||
|
private readonly IDatabaseDiscovery _mySqlDiscovery;
|
||||||
|
|
||||||
|
public DatabaseType SelectedDatabaseType { get; set; } = DatabaseType.SqlServer;
|
||||||
|
public string ConnectionString { get; set; }
|
||||||
|
public List<string> AvailableDatabases { get; private set; } = new List<string>();
|
||||||
|
public Dictionary<string, DatabaseInfo> DatabasesInfo { get; private set; } = new Dictionary<string, DatabaseInfo>();
|
||||||
|
public string SelectedDatabase { get; set; }
|
||||||
|
public bool IsConnected { get; private set; }
|
||||||
|
public string ErrorMessage { get; private set; }
|
||||||
|
|
||||||
|
public DatabaseConnectionService()
|
||||||
|
{
|
||||||
|
_sqlServerDiscovery = new SqlServerDatabaseDiscovery();
|
||||||
|
// Se in futuro verrà implementata la discovery MySQL, potremmo aggiungerla qui
|
||||||
|
// _mySqlDiscovery = new MySqlDatabaseDiscovery();
|
||||||
|
}
|
||||||
|
|
||||||
|
public IDatabaseDiscovery GetDatabaseDiscovery()
|
||||||
|
{
|
||||||
|
return SelectedDatabaseType switch
|
||||||
|
{
|
||||||
|
DatabaseType.SqlServer => _sqlServerDiscovery,
|
||||||
|
// DatabaseType.MySql => _mySqlDiscovery,
|
||||||
|
_ => throw new NotSupportedException($"Tipo di database non supportato: {SelectedDatabaseType}")
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> TestConnectionAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var discovery = GetDatabaseDiscovery();
|
||||||
|
AvailableDatabases = await discovery.GetAvailableDatabasesAsync(ConnectionString, true);
|
||||||
|
IsConnected = AvailableDatabases.Any();
|
||||||
|
ErrorMessage = IsConnected ? null : "Nessun database trovato sul server.";
|
||||||
|
return IsConnected;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
IsConnected = false;
|
||||||
|
ErrorMessage = $"Errore di connessione: {ex.Message}";
|
||||||
|
if (ex.InnerException != null)
|
||||||
|
{
|
||||||
|
ErrorMessage += $" - {ex.InnerException.Message}";
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<Dictionary<string, DatabaseInfo>> GetDatabasesInfoAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var discovery = GetDatabaseDiscovery();
|
||||||
|
DatabasesInfo = await discovery.GetDatabasesInfoAsync(ConnectionString, false);
|
||||||
|
return DatabasesInfo;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
ErrorMessage = $"Errore nel recupero delle informazioni: {ex.Message}";
|
||||||
|
return new Dictionary<string, DatabaseInfo>();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string BuildConnectionStringWithDatabase()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(SelectedDatabase))
|
||||||
|
return ConnectionString;
|
||||||
|
|
||||||
|
// Per SQL Server
|
||||||
|
if (SelectedDatabaseType == DatabaseType.SqlServer)
|
||||||
|
{
|
||||||
|
if (ConnectionString.Contains("Initial Catalog=") || ConnectionString.Contains("Database="))
|
||||||
|
{
|
||||||
|
// Sostituisci il database esistente
|
||||||
|
var modifiedString = System.Text.RegularExpressions.Regex.Replace(
|
||||||
|
ConnectionString,
|
||||||
|
@"(Initial Catalog|Database)=([^;]*)",
|
||||||
|
$"$1={SelectedDatabase}");
|
||||||
|
|
||||||
|
return modifiedString;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Aggiungi il database
|
||||||
|
return ConnectionString + $";Database={SelectedDatabase}";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Per altri tipi di database, implementare la logica appropriata
|
||||||
|
return ConnectionString;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,4 +6,8 @@
|
|||||||
<ImplicitUsings>enable</ImplicitUsings>
|
<ImplicitUsings>enable</ImplicitUsings>
|
||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\DataConnection\DataConnection.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -0,0 +1,296 @@
|
|||||||
|
@page "/database-connection"
|
||||||
|
@using DataConnection.Enums
|
||||||
|
@using DataConnection.Interfaces
|
||||||
|
@inject Data.DatabaseConnectionService DatabaseService
|
||||||
|
@inject IJSRuntime JSRuntime
|
||||||
|
|
||||||
|
<PageTitle>Connessione al Database</PageTitle>
|
||||||
|
|
||||||
|
<h3>Connessione al Database</h3>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
Configurazione connessione
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="databaseType" class="form-label">Tipo di Database</label>
|
||||||
|
<select id="databaseType" class="form-select" @bind="DatabaseService.SelectedDatabaseType">
|
||||||
|
<option value="@DatabaseType.SqlServer">SQL Server</option>
|
||||||
|
<option value="@DatabaseType.MySql">MySQL</option>
|
||||||
|
<option value="@DatabaseType.PostgreSql">PostgreSQL</option>
|
||||||
|
<option value="@DatabaseType.Oracle">Oracle</option>
|
||||||
|
<option value="@DatabaseType.Sqlite">SQLite</option>
|
||||||
|
<option value="@DatabaseType.DB2">DB2</option>
|
||||||
|
<option value="@DatabaseType.SapHana">SAP HANA</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="connectionString" class="form-label">Stringa di Connessione</label>
|
||||||
|
<input id="connectionString" class="form-control" @bind="DatabaseService.ConnectionString" placeholder="Es. Server=localhost;User Id=sa;Password=password;TrustServerCertificate=True" />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="d-grid gap-2">
|
||||||
|
<button class="btn btn-primary" @onclick="TestConnection" disabled="@isLoading">
|
||||||
|
@if (isLoading)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span> Connessione in corso...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>Test Connessione</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (DatabaseService.ErrorMessage != null)
|
||||||
|
{
|
||||||
|
<div class="alert alert-danger mt-3">
|
||||||
|
@DatabaseService.ErrorMessage
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (DatabaseService.IsConnected)
|
||||||
|
{
|
||||||
|
<div class="col-md-6">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
Database Disponibili
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
<div class="mb-3">
|
||||||
|
<label for="selectDatabase" class="form-label">Seleziona Database</label>
|
||||||
|
<select id="selectDatabase" class="form-select" @bind="DatabaseService.SelectedDatabase">
|
||||||
|
<option value="">-- Seleziona un database --</option>
|
||||||
|
@foreach (var db in DatabaseService.AvailableDatabases)
|
||||||
|
{
|
||||||
|
<option value="@db">@db</option>
|
||||||
|
}
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (!string.IsNullOrEmpty(DatabaseService.SelectedDatabase) && databaseInfo != null)
|
||||||
|
{
|
||||||
|
<div class="mt-3">
|
||||||
|
<h5>Dettagli Database</h5>
|
||||||
|
<table class="table table-bordered">
|
||||||
|
<tbody>
|
||||||
|
<tr>
|
||||||
|
<th>Nome</th>
|
||||||
|
<td>@databaseInfo?.Name</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Dimensione</th>
|
||||||
|
<td>@databaseInfo?.SizeMB MB</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Data Creazione</th>
|
||||||
|
<td>@databaseInfo?.CreationDate.ToString("dd/MM/yyyy HH:mm")</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Stato</th>
|
||||||
|
<td>@databaseInfo?.Status</td>
|
||||||
|
</tr>
|
||||||
|
<tr>
|
||||||
|
<th>Proprietario</th>
|
||||||
|
<td>@databaseInfo?.Owner</td>
|
||||||
|
</tr>
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
|
<div class="d-grid gap-2 mt-3">
|
||||||
|
<button class="btn btn-success" @onclick="ConnectToDatabase" disabled="@isConnecting">
|
||||||
|
@if (isConnecting)
|
||||||
|
{
|
||||||
|
<span class="spinner-border spinner-border-sm" role="status" aria-hidden="true"></span>
|
||||||
|
<span> Connessione in corso...</span>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<span>Connetti al Database</span>
|
||||||
|
}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
@if (showSchemaInfo)
|
||||||
|
{
|
||||||
|
<div class="row mt-4">
|
||||||
|
<div class="col-12">
|
||||||
|
<div class="card">
|
||||||
|
<div class="card-header">
|
||||||
|
Schema del Database: @DatabaseService.SelectedDatabase
|
||||||
|
</div>
|
||||||
|
<div class="card-body">
|
||||||
|
@if (schemaInfo != null && schemaInfo.Count > 0)
|
||||||
|
{
|
||||||
|
<div class="table-responsive">
|
||||||
|
<table class="table table-striped table-hover">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Tabella</th>
|
||||||
|
<th>Colonne</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody>
|
||||||
|
@foreach (var table in schemaInfo)
|
||||||
|
{
|
||||||
|
<tr>
|
||||||
|
<td>@table.Key</td>
|
||||||
|
<td>
|
||||||
|
<ul class="list-unstyled">
|
||||||
|
@foreach (var column in table.Value)
|
||||||
|
{
|
||||||
|
<li>
|
||||||
|
<strong>@column.Name</strong>
|
||||||
|
<span class="text-muted">(@column.DataType)</span>
|
||||||
|
@if (column.IsPrimaryKey)
|
||||||
|
{
|
||||||
|
<span class="badge bg-primary">PK</span>
|
||||||
|
}
|
||||||
|
@if (column.IsForeignKey)
|
||||||
|
{
|
||||||
|
<span class="badge bg-info">FK → @column.ReferencedTable</span>
|
||||||
|
}
|
||||||
|
</li>
|
||||||
|
}
|
||||||
|
</ul>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
<div class="alert alert-info">
|
||||||
|
Nessuna informazione sullo schema disponibile.
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
|
||||||
|
@code {
|
||||||
|
private bool isLoading = false;
|
||||||
|
private bool isConnecting = false;
|
||||||
|
private bool showSchemaInfo = false;
|
||||||
|
private DatabaseInfo databaseInfo;
|
||||||
|
private IDictionary<string, IEnumerable<DbColumnInfo>> schemaInfo;
|
||||||
|
|
||||||
|
protected override async Task OnInitializedAsync()
|
||||||
|
{
|
||||||
|
// Puoi impostare valori predefiniti, magari da configurazione
|
||||||
|
DatabaseService.ConnectionString = "Server=localhost;User Id=sa;Password=password;TrustServerCertificate=True";
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task TestConnection()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
isLoading = true;
|
||||||
|
showSchemaInfo = false;
|
||||||
|
databaseInfo = null;
|
||||||
|
|
||||||
|
await DatabaseService.TestConnectionAsync();
|
||||||
|
|
||||||
|
if (DatabaseService.IsConnected)
|
||||||
|
{
|
||||||
|
await DatabaseService.GetDatabasesInfoAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await JSRuntime.InvokeVoidAsync("console.error", "Errore connessione:", ex.Message);
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
isLoading = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task ConnectToDatabase()
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(DatabaseService.SelectedDatabase))
|
||||||
|
return;
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
isConnecting = true;
|
||||||
|
showSchemaInfo = false;
|
||||||
|
|
||||||
|
// Ottieni informazioni sul database selezionato
|
||||||
|
if (DatabaseService.DatabasesInfo.TryGetValue(DatabaseService.SelectedDatabase, out var dbInfo))
|
||||||
|
{
|
||||||
|
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
|
||||||
|
|
||||||
|
showSchemaInfo = true;
|
||||||
|
// In un'implementazione reale, qui dovresti eseguire:
|
||||||
|
// schemaInfo = await databaseManager.GetDatabaseSchemaAsync();
|
||||||
|
|
||||||
|
// Per ora creiamo dati di esempio
|
||||||
|
schemaInfo = CreateSampleSchemaInfo();
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
await JSRuntime.InvokeVoidAsync("console.error", "Errore connessione database:", ex.Message);
|
||||||
|
//DatabaseService.ErrorMessage = $"Errore nella connessione al database: {ex.Message}";
|
||||||
|
showSchemaInfo = false;
|
||||||
|
}
|
||||||
|
finally
|
||||||
|
{
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,13 @@
|
|||||||
using Microsoft.AspNetCore.Components;
|
using Microsoft.AspNetCore.Components;
|
||||||
using Microsoft.AspNetCore.Components.Web;
|
using Microsoft.AspNetCore.Components.Web;
|
||||||
using Data_Coupler.Data;
|
using Data_Coupler.Data;
|
||||||
|
using DataConnection;
|
||||||
|
using DataConnection.EF;
|
||||||
|
using DataConnection.Interfaces;
|
||||||
|
using DataConnection.EF.DatabaseDiscovery;
|
||||||
|
using DataConnection.Enums;
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
var builder = WebApplication.CreateBuilder(args);
|
var builder = WebApplication.CreateBuilder(args);
|
||||||
|
|
||||||
@@ -9,8 +16,16 @@ builder.Services.AddRazorPages();
|
|||||||
builder.Services.AddServerSideBlazor();
|
builder.Services.AddServerSideBlazor();
|
||||||
builder.Services.AddSingleton<WeatherForecastService>();
|
builder.Services.AddSingleton<WeatherForecastService>();
|
||||||
|
|
||||||
|
// Registra il servizio di connessione al database
|
||||||
|
builder.Services.AddScoped<DatabaseConnectionService>();
|
||||||
|
|
||||||
|
// Registriamo i servizi di database discovery
|
||||||
|
builder.Services.AddSingleton<IDatabaseDiscovery, SqlServerDatabaseDiscovery>();
|
||||||
|
builder.Services.AddSingleton<DbManagerOptions>();
|
||||||
|
|
||||||
var app = builder.Build();
|
var app = builder.Build();
|
||||||
|
|
||||||
|
|
||||||
// Configure the HTTP request pipeline.
|
// Configure the HTTP request pipeline.
|
||||||
if (!app.Environment.IsDevelopment())
|
if (!app.Environment.IsDevelopment())
|
||||||
{
|
{
|
||||||
@@ -29,3 +44,4 @@ app.MapBlazorHub();
|
|||||||
app.MapFallbackToPage("/_Host");
|
app.MapFallbackToPage("/_Host");
|
||||||
|
|
||||||
app.Run();
|
app.Run();
|
||||||
|
|
||||||
|
|||||||
@@ -24,6 +24,11 @@
|
|||||||
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
|
<span class="oi oi-list-rich" aria-hidden="true"></span> Fetch data
|
||||||
</NavLink>
|
</NavLink>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="nav-item px-3">
|
||||||
|
<NavLink class="nav-link" href="database-connection">
|
||||||
|
<span class="oi oi-hard-drive" aria-hidden="true"></span> Database
|
||||||
|
</NavLink>
|
||||||
|
</div>
|
||||||
</nav>
|
</nav>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,8 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<configuration>
|
||||||
|
<packageSources>
|
||||||
|
<!--To inherit the global NuGet package sources remove the <clear/> line below -->
|
||||||
|
<clear />
|
||||||
|
<add key="nuget" value="https://api.nuget.org/v3/index.json" />
|
||||||
|
</packageSources>
|
||||||
|
</configuration>
|
||||||
Reference in New Issue
Block a user