a873dce31b
- Modificato GetAllRecordsAsync per utilizzare la stessa connection string del discovery schema - Aggiunto metodo CreateConnection per creare connessioni DB appropriate per tipo - Migliorata gestione nomi tabelle con schema (es. "dbo.OCRD") - Rimossi metodi obsoleti di creazione entità (UpdateEntityData, CreateNewEntity) - Eliminati riferimenti a variabili non dichiarate (newEntityData, isCreatingEntity) - Aggiunto logging debug per connection string e query SQL - Completata implementazione trasferimento dati da database a REST API Il trasferimento dati ora utilizza la stessa connessione per discovery e estrazione, risolvendo problemi di accesso alle tabelle durante l'operazione di upsert.
226 lines
8.1 KiB
C#
226 lines
8.1 KiB
C#
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;
|
|
|
|
/// <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()
|
|
{
|
|
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<IEnumerable<Dictionary<string, object>>> GetAllRecordsAsync(string tableName)
|
|
{
|
|
try
|
|
{
|
|
var records = new List<Dictionary<string, object>>();
|
|
|
|
// 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<string, object>();
|
|
|
|
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;
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Crea una connessione database appropriata in base al tipo di database
|
|
/// </summary>
|
|
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();
|
|
}
|
|
}
|