using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
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;
using Microsoft.Data.SqlClient;
namespace DataConnection.EF;
///
/// Implementazione di IExistingDatabaseManager basata su Entity Framework Core
///
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();
_options.DbContextConfigurator(optionsBuilder);
_context = new ExistingDatabaseContext(
optionsBuilder.Options,
_options.ModelConfigurator,
_options.EnableAutoDiscovery,
_options.EntityAssembly,
_options.EntityNamespace,
_options.NamingStrategy);
}
public async Task TestConnectionAsync()
{
try
{
return await _context.Database.CanConnectAsync();
}
catch
{
return false;
}
}
public async Task> GetAsync(
Expression> filter = null,
Func, IOrderedQueryable> orderBy = null,
string includeProperties = "",
int? skip = null,
int? take = null) where T : class
{
IQueryable query = _context.Set();
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 GetByIdAsync(object id) where T : class
{
return await _context.Set().FindAsync(id);
}
public async Task> ExecuteQueryAsync(string sql, params object[] parameters) where T : class
{
return await _context.Set().FromSqlRaw(sql, parameters).ToListAsync();
}
public async Task ExecuteCommandAsync(string sql, params object[] parameters)
{
return await _context.Database.ExecuteSqlRawAsync(sql, parameters);
}
public async Task>> GetDatabaseSchemaAsync()
{
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}");
}
}
return result;
}
catch (Exception ex)
{
Console.WriteLine($"Errore nel recupero dello schema del database: {ex.Message}");
Console.WriteLine($"[DEBUG] Stack trace: {ex.StackTrace}");
throw; }
} public async Task>> GetAllRecordsAsync(string tableName)
{
try
{
var records = new List>();
// 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))}...");
// Determina il tipo di connessione in base al DatabaseType
using var connection = CreateConnection(connectionString);
await connection.OpenAsync();
using var command = connection.CreateCommand();
// Query SQL semplice per ottenere tutti i record - limitiamo a 1000 per sicurezza
// Se il nome della tabella contiene già lo schema (es. "dbo.OCRD"), lo usiamo così com'è
// Altrimenti aggiungiamo le parentesi quadre
string tableReference;
if (tableName.Contains('.'))
{
// Il nome contiene già lo schema, separiamo e mettiamo entrambi tra parentesi quadre
var parts = tableName.Split('.');
tableReference = $"[{parts[0]}].[{parts[1]}]";
}
else
{
// Solo il nome della tabella, usiamo le parentesi quadre
tableReference = $"[{tableName}]";
}
command.CommandText = $"SELECT TOP 1000 * FROM {tableReference}";
Console.WriteLine($"[DEBUG] GetAllRecordsAsync - Query: {command.CommandText}");
using var reader = await command.ExecuteReaderAsync();
while (await reader.ReadAsync())
{
var record = new Dictionary();
for (int i = 0; i < reader.FieldCount; i++)
{
var columnName = reader.GetName(i);
var value = reader.IsDBNull(i) ? null : reader.GetValue(i);
record[columnName] = value;
}
records.Add(record);
}
Console.WriteLine($"[DEBUG] GetAllRecordsAsync - Tabella: {tableName}, Record ottenuti: {records.Count}");
return records;
}
catch (Exception ex)
{
Console.WriteLine($"Errore nell'ottenere i record dalla tabella {tableName}: {ex.Message}");
throw;
}
}
///
/// Crea una connessione database appropriata in base al tipo di database
///
private DbConnection CreateConnection(string connectionString)
{
switch (_options.DatabaseType)
{
case Enums.DatabaseType.SqlServer:
return new SqlConnection(connectionString);
// Aggiungi altri tipi di database quando necessario
// case Enums.DatabaseType.MySQL:
// return new MySqlConnection(connectionString);
// case Enums.DatabaseType.PostgreSQL:
// return new NpgsqlConnection(connectionString);
default:
throw new NotSupportedException($"Database type {_options.DatabaseType} is not supported for direct connections");
}
}
public void Dispose()
{
_context?.Dispose();
}
}