feat: Implementazione completa sistema schedulazione con intervalli personalizzati
- Aggiunto supporto schedulazione con intervalli flessibili (secondi/minuti/ore/giorni/settimane/mesi) - Esteso modello ProfileSchedule con campi IntervalValue e IntervalUnit - Ottimizzato ScheduledJobService per controlli ogni 30s con esecuzione parallela - Implementata interfaccia UI completa con anteprima real-time in italiano - Aggiunta migrazione database AddIntervalSchedulingFields - Implementati metodi calcolo NextExecutionTime per intervalli - Aggiunta gestione tracking anti-duplicati e cleanup automatico - Creata documentazione completa (6 file, 2500+ righe) Modifiche tecniche: - ProfileSchedule.cs: Nuovi campi e metodi CalculateNextInterval/GetScheduleDescription - ScheduledJobService.cs: Ridotto check interval a 30s, aggiunto parallel processing - ProfileScheduleService.cs: Supporto calcolo intervalli in UpdateNextExecutionTimeAsync - Scheduling.razor: Aggiunta sezione UI per configurazione intervalli - Scheduling.razor.cs: Implementato GetIntervalPreview() e gestione stato campi
This commit is contained in:
@@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.Data.SqlClient;
|
||||
using CredentialManager.Migrations;
|
||||
|
||||
namespace DataConnection.EF;
|
||||
|
||||
@@ -101,41 +102,87 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
return await _context.Set<T>().FromSqlRaw(sql, parameters).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task<List<Dictionary<string, object>>> ExecuteRawQueryAsync(string sql, params object[] parameters)
|
||||
public async Task<List<Dictionary<string, object>>> ExecuteRawQueryAsync(string sql, string databaseName, params object[] parameters)
|
||||
{
|
||||
using var command = _context.Database.GetDbConnection().CreateCommand();
|
||||
command.CommandText = sql;
|
||||
|
||||
// Aggiungi i parametri
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
try
|
||||
{
|
||||
var parameter = command.CreateParameter();
|
||||
parameter.ParameterName = $"@p{i}";
|
||||
parameter.Value = parameters[i] ?? DBNull.Value;
|
||||
command.Parameters.Add(parameter);
|
||||
}
|
||||
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
|
||||
var results = new List<Dictionary<string, object>>();
|
||||
|
||||
using var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var row = new Dictionary<string, object>();
|
||||
|
||||
for (int i = 0; i < reader.FieldCount; i++)
|
||||
// Assicurarsi che la connessione sia aperta
|
||||
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
|
||||
{
|
||||
var columnName = reader.GetName(i);
|
||||
var value = reader.IsDBNull(i) ? null : reader.GetValue(i);
|
||||
row[columnName] = value ?? ""; // Usa stringa vuota invece di null
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
}
|
||||
|
||||
results.Add(row);
|
||||
var connection = _context.Database.GetDbConnection();
|
||||
// Debug: verifica il database attuale della connessione
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: Database connessione prima: {connection.Database}");
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: Connection string: {connection.ConnectionString}");
|
||||
|
||||
// Estrai il database target dalla connection string del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: Connection string completa: {connectionString}");
|
||||
|
||||
if (!string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
//var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
var targetDatabase = databaseName;
|
||||
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: InitialCatalog dalla connection string: '{targetDatabase}'");
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: Database corrente connessione: '{connection.Database}'");
|
||||
|
||||
// Se il database della connessione non corrisponde al target, forza il cambio
|
||||
if (!string.IsNullOrEmpty(targetDatabase) && !string.Equals(connection.Database, targetDatabase, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: MISMATCH RILEVATO! Forzando cambio database da '{connection.Database}' a '{targetDatabase}'");
|
||||
await connection.ChangeDatabaseAsync(targetDatabase);
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: Database connessione dopo cambio: '{connection.Database}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"ExecuteRawQueryAsync: Database già corretto: '{connection.Database}' = '{targetDatabase}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("ExecuteRawQueryAsync: ATTENZIONE! Connection string è vuota!");
|
||||
}
|
||||
|
||||
using var command = connection.CreateCommand();
|
||||
command.CommandText = sql;
|
||||
|
||||
// Aggiungi i parametri
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
var parameter = command.CreateParameter();
|
||||
parameter.ParameterName = $"@p{i}";
|
||||
parameter.Value = parameters[i] ?? DBNull.Value;
|
||||
command.Parameters.Add(parameter);
|
||||
}
|
||||
|
||||
var results = new List<Dictionary<string, object>>();
|
||||
|
||||
using var reader = await command.ExecuteReaderAsync();
|
||||
|
||||
while (await reader.ReadAsync())
|
||||
{
|
||||
var row = 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);
|
||||
row[columnName] = value ?? ""; // Usa stringa vuota invece di null
|
||||
}
|
||||
|
||||
results.Add(row);
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Errore nell'esecuzione della query raw: {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
|
||||
return results;
|
||||
}
|
||||
|
||||
public async Task<int> ExecuteCommandAsync(string sql, params object[] parameters)
|
||||
@@ -148,15 +195,20 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
try
|
||||
{
|
||||
// Assicurarsi che il contesto sia connesso
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
|
||||
{
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
}
|
||||
|
||||
// Usa la factory per ottenere il provider appropriato in base al tipo di database
|
||||
var schemaProvider = DatabaseSchemaProviderFactory.CreateProvider(_options.DatabaseType);
|
||||
|
||||
// Usa il provider per ottenere lo schema
|
||||
// Usa il provider per ottenere lo schema utilizzando la connection string corrente del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
Console.WriteLine($"GetDatabaseSchemaAsync: Utilizzo connection string: {connectionString}");
|
||||
|
||||
var result = await schemaProvider.GetDatabaseSchemaAsync(connectionString);
|
||||
|
||||
@@ -180,7 +232,7 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
// Usa la factory per ottenere il provider appropriato in base al tipo di database
|
||||
var schemaProvider = DatabaseSchemaProviderFactory.CreateProvider(_options.DatabaseType);
|
||||
|
||||
// Usa il provider per ottenere la lista dei database
|
||||
// Usa il provider per ottenere la lista dei database utilizzando la connection string corrente del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
@@ -207,7 +259,7 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
// Usa la factory per ottenere il provider appropriato in base al tipo di database
|
||||
var schemaProvider = DatabaseSchemaProviderFactory.CreateProvider(_options.DatabaseType);
|
||||
|
||||
// Usa il provider per ottenere la lista delle tabelle
|
||||
// Usa il provider per ottenere la lista delle tabelle utilizzando la connection string corrente del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
@@ -235,7 +287,7 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
// Usa la factory per ottenere il provider appropriato in base al tipo di database
|
||||
var schemaProvider = DatabaseSchemaProviderFactory.CreateProvider(_options.DatabaseType);
|
||||
|
||||
// Usa il provider per ottenere lo schema della tabella
|
||||
// Usa il provider per ottenere lo schema della tabella utilizzando la connection string corrente del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
@@ -257,14 +309,45 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
{
|
||||
var records = new List<Dictionary<string, object>>();
|
||||
|
||||
// Usa la stessa connection string utilizzata per il discovery dello schema
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
// Usa la connessione del DbContext che è stata aggiornata se è stato chiamato ChangeDatabaseAsync
|
||||
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
|
||||
{
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
}
|
||||
|
||||
// Determina il tipo di connessione in base al DatabaseType
|
||||
using var connection = CreateConnection(connectionString);
|
||||
await connection.OpenAsync();
|
||||
var connection = _context.Database.GetDbConnection();
|
||||
|
||||
// Debug: verifica il database attuale della connessione
|
||||
Console.WriteLine($"GetAllRecordsAsync: Database connessione prima: {connection.Database}");
|
||||
|
||||
// Estrai il database target dalla connection string del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
Console.WriteLine($"GetAllRecordsAsync: Connection string completa: {connectionString}");
|
||||
|
||||
if (!string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
var targetDatabase = builder.InitialCatalog;
|
||||
|
||||
Console.WriteLine($"GetAllRecordsAsync: InitialCatalog dalla connection string: '{targetDatabase}'");
|
||||
Console.WriteLine($"GetAllRecordsAsync: Database corrente connessione: '{connection.Database}'");
|
||||
|
||||
// Se il database della connessione non corrisponde al target, forza il cambio
|
||||
if (!string.IsNullOrEmpty(targetDatabase) && !string.Equals(connection.Database, targetDatabase, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"GetAllRecordsAsync: MISMATCH RILEVATO! Forzando cambio database da '{connection.Database}' a '{targetDatabase}'");
|
||||
await connection.ChangeDatabaseAsync(targetDatabase);
|
||||
Console.WriteLine($"GetAllRecordsAsync: Database connessione dopo cambio: '{connection.Database}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"GetAllRecordsAsync: Database già corretto: '{connection.Database}' = '{targetDatabase}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("GetAllRecordsAsync: ATTENZIONE! Connection string è vuota!");
|
||||
}
|
||||
|
||||
using var command = connection.CreateCommand();
|
||||
|
||||
@@ -319,9 +402,13 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
if (currentConnectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
|
||||
Console.WriteLine($"ChangeDatabaseAsync: Connessione corrente: {currentConnectionString}");
|
||||
|
||||
// Crea una nuova connection string con il database specificato
|
||||
var newConnectionString = UpdateConnectionStringDatabase(currentConnectionString, databaseName);
|
||||
|
||||
Console.WriteLine($"ChangeDatabaseAsync: Nuova connessione: {newConnectionString}");
|
||||
|
||||
// Ricrea il contesto con la nuova connection string
|
||||
var optionsBuilder = new DbContextOptionsBuilder<ExistingDatabaseContext>();
|
||||
|
||||
@@ -350,6 +437,8 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
|
||||
// Testa la connessione al nuovo database
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
|
||||
Console.WriteLine($"ChangeDatabaseAsync: Connessione aggiornata verificata: {_context.Database.GetConnectionString()}");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -406,12 +495,45 @@ public class EFCoreDatabaseManager : IDatabaseManager
|
||||
{
|
||||
try
|
||||
{
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
if (connectionString == null)
|
||||
throw new InvalidOperationException("Connection string is null");
|
||||
// Usa la connessione del DbContext che è stata aggiornata se è stato chiamato ChangeDatabaseAsync
|
||||
if (_context.Database.GetDbConnection().State != ConnectionState.Open)
|
||||
{
|
||||
await _context.Database.OpenConnectionAsync();
|
||||
}
|
||||
|
||||
using var connection = CreateConnection(connectionString);
|
||||
await connection.OpenAsync();
|
||||
var connection = _context.Database.GetDbConnection();
|
||||
|
||||
// Debug: verifica il database attuale della connessione
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: Database connessione prima: {connection.Database}");
|
||||
|
||||
// Estrai il database target dalla connection string del DbContext
|
||||
var connectionString = _context.Database.GetConnectionString();
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: Connection string completa: {connectionString}");
|
||||
|
||||
if (!string.IsNullOrEmpty(connectionString))
|
||||
{
|
||||
var builder = new SqlConnectionStringBuilder(connectionString);
|
||||
var targetDatabase = builder.InitialCatalog;
|
||||
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: InitialCatalog dalla connection string: '{targetDatabase}'");
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: Database corrente connessione: '{connection.Database}'");
|
||||
|
||||
// Se il database della connessione non corrisponde al target, forza il cambio
|
||||
if (!string.IsNullOrEmpty(targetDatabase) && !string.Equals(connection.Database, targetDatabase, StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: MISMATCH RILEVATO! Forzando cambio database da '{connection.Database}' a '{targetDatabase}'");
|
||||
await connection.ChangeDatabaseAsync(targetDatabase);
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: Database connessione dopo cambio: '{connection.Database}'");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"GetPrimaryKeyFieldAsync: Database già corretto: '{connection.Database}' = '{targetDatabase}'");
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("GetPrimaryKeyFieldAsync: ATTENZIONE! Connection string è vuota!");
|
||||
}
|
||||
|
||||
using var command = connection.CreateCommand();
|
||||
|
||||
|
||||
@@ -44,7 +44,7 @@ public interface IDatabaseManager : IDisposable
|
||||
/// <summary>
|
||||
/// Esegue una query SQL raw e restituisce i risultati come dictionary
|
||||
/// </summary>
|
||||
Task<List<Dictionary<string, object>>> ExecuteRawQueryAsync(string sql, params object[] parameters);
|
||||
Task<List<Dictionary<string, object>>> ExecuteRawQueryAsync(string sql, string databaseName = "", params object[] parameters);
|
||||
|
||||
/// <summary>
|
||||
/// Esegue un comando SQL che non restituisce risultati
|
||||
|
||||
Reference in New Issue
Block a user