51c61eabf7
- Aggiunge rilevamento automatico Primary Key per connessioni database - Rimuove completamente il fallback automatico per lato sorgente - Implementa selezione manuale obbligatoria per file e sorgenti non-DB - Migliora UI con suggerimenti intelligenti e feedback visivo - Aggiunge validazione multi-livello (UI, pre-transfer, runtime) - Introduce metodo GetPrimaryKeyFieldAsync in IDatabaseManager - Modifica GenerateSourceKey per richiedere sempre campo specifico - Implementa controllo IsTransferButtonEnabled per validazione form Breaking changes: - La generazione automatica delle chiavi sorgente è stata rimossa - Il campo chiave sorgente è ora obbligatorio quando si usa il sistema associazioni Fixes: Risolve problema di discovery schema vuoto con selezione database
292 lines
11 KiB
C#
292 lines
11 KiB
C#
using Microsoft.EntityFrameworkCore;
|
|
using Microsoft.Extensions.Logging;
|
|
using CredentialManager.Data;
|
|
|
|
namespace CredentialManager.Services;
|
|
|
|
/// <summary>
|
|
/// Interfaccia per l'inizializzazione del database
|
|
/// </summary>
|
|
public interface IDatabaseInitializer
|
|
{
|
|
Task InitializeAsync();
|
|
Task<bool> DatabaseExistsAsync();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Servizio per l'inizializzazione del database SQLite
|
|
/// </summary>
|
|
public class DatabaseInitializer : IDatabaseInitializer
|
|
{
|
|
private readonly CredentialDbContext _context;
|
|
private readonly ILogger<DatabaseInitializer> _logger;
|
|
|
|
public DatabaseInitializer(CredentialDbContext context, ILogger<DatabaseInitializer> logger)
|
|
{
|
|
_context = context;
|
|
_logger = logger;
|
|
} public async Task InitializeAsync()
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation("Inizializzazione database in corso...");
|
|
|
|
// Per SQLite con EnsureCreatedAsync è più semplice e sicuro
|
|
// Crea il database e le tabelle se non esistono
|
|
var created = await _context.Database.EnsureCreatedAsync();
|
|
|
|
if (created)
|
|
{
|
|
_logger.LogInformation("Database creato con successo");
|
|
|
|
// Aggiungi dati di esempio se necessario
|
|
await SeedInitialDataAsync();
|
|
}
|
|
else
|
|
{
|
|
_logger.LogInformation("Database esistente trovato");
|
|
|
|
// Verifica che le tabelle esistano
|
|
await VerifyTablesAsync();
|
|
|
|
// Applica migrazioni se necessario
|
|
await ApplyMigrationsAsync();
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore durante l'inizializzazione del database");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private async Task VerifyTablesAsync()
|
|
{
|
|
try
|
|
{
|
|
// Verifica che la tabella principale Credentials esista
|
|
await _context.Credentials.CountAsync();
|
|
_logger.LogInformation("Tabella Credentials verificata con successo");
|
|
|
|
// Verifica se la tabella RecordAssociations esiste, se non esiste la crea senza ricreare tutto il database
|
|
try
|
|
{
|
|
await _context.RecordAssociations.CountAsync();
|
|
_logger.LogInformation("Tabella RecordAssociations verificata con successo");
|
|
}
|
|
catch (Exception)
|
|
{
|
|
_logger.LogInformation("Tabella RecordAssociations non trovata, creazione tramite migrazione...");
|
|
await CreateRecordAssociationsTableAsync();
|
|
}
|
|
}
|
|
catch (Exception)
|
|
{
|
|
_logger.LogWarning("Tabella Credentials mancante, ricreazione database...");
|
|
|
|
// Solo se la tabella principale non esiste, ricreiamo tutto
|
|
await _context.Database.EnsureDeletedAsync();
|
|
await _context.Database.EnsureCreatedAsync();
|
|
await SeedInitialDataAsync();
|
|
|
|
_logger.LogInformation("Database ricreato con successo");
|
|
}
|
|
}
|
|
|
|
public async Task<bool> DatabaseExistsAsync()
|
|
{
|
|
try
|
|
{
|
|
return await _context.Database.CanConnectAsync();
|
|
}
|
|
catch
|
|
{
|
|
return false;
|
|
}
|
|
}
|
|
|
|
private async Task SeedInitialDataAsync()
|
|
{
|
|
try
|
|
{
|
|
// Verifica se ci sono già dati
|
|
if (await _context.Credentials.AnyAsync())
|
|
{
|
|
_logger.LogInformation("Dati esistenti trovati. Seeding saltato.");
|
|
return;
|
|
}
|
|
|
|
_logger.LogInformation("Aggiunta dati di esempio...");
|
|
|
|
// Qui puoi aggiungere dati di esempio se necessario
|
|
// Ad esempio:
|
|
/*
|
|
var sampleCredentials = new List<CredentialEntity>
|
|
{
|
|
new CredentialEntity
|
|
{
|
|
Name = "Esempio Database",
|
|
Type = "Database",
|
|
Host = "localhost",
|
|
Port = 1433,
|
|
DatabaseName = "TestDB",
|
|
Username = "testuser",
|
|
CreatedAt = DateTime.UtcNow,
|
|
CreatedBy = "System",
|
|
IsActive = true
|
|
}
|
|
};
|
|
|
|
_context.Credentials.AddRange(sampleCredentials);
|
|
await _context.SaveChangesAsync();
|
|
*/
|
|
|
|
_logger.LogInformation("Dati di esempio aggiunti con successo");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore durante il seeding dei dati iniziali");
|
|
throw;
|
|
}
|
|
} private async Task ApplyMigrationsAsync()
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation("Verifica e applicazione migrazioni...");
|
|
|
|
// Migrazione 1: Verifica se la colonna RestServiceType esiste
|
|
try
|
|
{
|
|
await _context.Database.ExecuteSqlRawAsync(
|
|
"SELECT RestServiceType FROM Credentials LIMIT 1");
|
|
_logger.LogInformation("Colonna RestServiceType già presente");
|
|
}
|
|
catch (Microsoft.Data.Sqlite.SqliteException)
|
|
{
|
|
// La colonna non esiste, la aggiungiamo
|
|
_logger.LogInformation("Aggiunta colonna RestServiceType...");
|
|
await _context.Database.ExecuteSqlRawAsync(
|
|
"ALTER TABLE Credentials ADD COLUMN RestServiceType TEXT");
|
|
_logger.LogInformation("Colonna RestServiceType aggiunta con successo");
|
|
}
|
|
|
|
// Migrazione 2: Verifica se la tabella RecordAssociations esiste
|
|
try
|
|
{
|
|
await _context.Database.ExecuteSqlRawAsync(
|
|
"SELECT COUNT(*) FROM RecordAssociations LIMIT 1");
|
|
_logger.LogInformation("Tabella RecordAssociations già presente");
|
|
}
|
|
catch (Microsoft.Data.Sqlite.SqliteException)
|
|
{
|
|
// La tabella non esiste, la creiamo
|
|
_logger.LogInformation("Creazione tabella RecordAssociations...");
|
|
|
|
// Crea la tabella
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE TABLE RecordAssociations (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
SourceName TEXT NOT NULL,
|
|
SourceType TEXT NOT NULL,
|
|
SourceKey TEXT NOT NULL,
|
|
DestinationEntity TEXT NOT NULL,
|
|
DestinationId TEXT NOT NULL,
|
|
RestCredentialName TEXT NOT NULL,
|
|
CreatedAt TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UpdatedAt TEXT,
|
|
IsActive INTEGER NOT NULL DEFAULT 1,
|
|
AdditionalInfo TEXT
|
|
)");
|
|
|
|
// Crea gli indici
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE UNIQUE INDEX IX_RecordAssociations_Unique
|
|
ON RecordAssociations (SourceName, SourceKey, DestinationEntity)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_SourceType
|
|
ON RecordAssociations (SourceType)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_DestinationEntity
|
|
ON RecordAssociations (DestinationEntity)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_RestCredentialName
|
|
ON RecordAssociations (RestCredentialName)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_IsActive
|
|
ON RecordAssociations (IsActive)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_CreatedAt
|
|
ON RecordAssociations (CreatedAt)");
|
|
|
|
_logger.LogInformation("Tabella RecordAssociations creata con successo");
|
|
}
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore nell'applicazione delle migrazioni");
|
|
throw;
|
|
}
|
|
}
|
|
|
|
private async Task CreateRecordAssociationsTableAsync()
|
|
{
|
|
try
|
|
{
|
|
_logger.LogInformation("Creazione tabella RecordAssociations...");
|
|
|
|
// Crea la tabella
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE TABLE RecordAssociations (
|
|
Id INTEGER PRIMARY KEY AUTOINCREMENT,
|
|
SourceName TEXT NOT NULL,
|
|
SourceType TEXT NOT NULL,
|
|
SourceKey TEXT NOT NULL,
|
|
DestinationEntity TEXT NOT NULL,
|
|
DestinationId TEXT NOT NULL,
|
|
RestCredentialName TEXT NOT NULL,
|
|
CreatedAt TEXT NOT NULL DEFAULT (datetime('now')),
|
|
UpdatedAt TEXT,
|
|
IsActive INTEGER NOT NULL DEFAULT 1,
|
|
AdditionalInfo TEXT
|
|
)");
|
|
|
|
// Crea gli indici
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE UNIQUE INDEX IX_RecordAssociations_Unique
|
|
ON RecordAssociations (SourceName, SourceKey, DestinationEntity)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_SourceType
|
|
ON RecordAssociations (SourceType)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_DestinationEntity
|
|
ON RecordAssociations (DestinationEntity)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_RestCredentialName
|
|
ON RecordAssociations (RestCredentialName)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_IsActive
|
|
ON RecordAssociations (IsActive)");
|
|
|
|
await _context.Database.ExecuteSqlRawAsync(@"
|
|
CREATE INDEX IX_RecordAssociations_CreatedAt
|
|
ON RecordAssociations (CreatedAt)");
|
|
|
|
_logger.LogInformation("Tabella RecordAssociations creata con successo");
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
_logger.LogError(ex, "Errore nella creazione della tabella RecordAssociations");
|
|
throw;
|
|
}
|
|
}
|
|
}
|