e70abcdcb1
Aggiunge un connettore completamente managed per database Visual FoxPro e dBase, utilizzabile come sorgente dati in tutti i flussi dell'applicazione (discovery manuale, esecuzione schedulata, hash/change-detection, backup/restore). ## Motivazione Il provider OLE DB ufficiale (VFPOLEDB.1) è solo a 32-bit, deprecato e richiede installazione separata. Il connettore managed usa DbfDataReader 1.0.0 (MIT), legge direttamente i file .dbf/.fpt/.dbc a 64-bit senza dipendenze esterne e con streaming efficiente (tabelle da centinaia di MB con uso di memoria contenuto). ## Nuovi file - DataConnection/DB/FoxPro/FoxProConnectionInfo.cs Classe sealed con le informazioni di connessione risolte (cartella, percorso .dbc, encoding, flag IncludeDeleted). Proprietà di convenienza IsContainer e DisplayName. - DataConnection/DB/FoxPro/FoxProReader.cs Helper statico che registra CodePagesEncodingProvider, risolve la connection string (path diretto, stile OLE DB con Provider=vfpoledb, chiave=valore), verifica l'accesso al filesystem, legge il catalogo .dbc (il file .dbc è esso stesso un DBF; filtro su OBJECTTYPE='Table' + cross-check esistenza fisica .dbf), enumera le tabelle free, mappa i tipi VFP (Character, Memo, Numeric, Float, Double, Integer, Currency, Date, DateTime, Logical, General) in descrizioni leggibili, esegue streaming dei record via DbfDataReader, auto-rileva l'encoding dall'header DBF (language driver byte) con fallback a code page 1252, e analizza query SELECT [TOP n] * FROM tabella. - DataConnection/DB/FoxProDatabaseManager.cs Implementa IDatabaseManager. Lettura: TestConnectionAsync, GetTableNamesAsync, GetTableSchemaAsync, GetDatabaseSchemaAsync, GetAllRecordsAsync, ExecuteRawQueryAsync, GetPrimaryKeyFieldAsync (ritorna null, selezione manuale chiave), GetAvailableDatabasesAsync, ChangeDatabaseAsync (no-op per sorgenti file-based). Scrittura: tutti i metodi di modifica (Insert/Update/Delete/Upsert/BulkInsert/ ExecuteCommand/ExecuteNonQuery) lanciano NotSupportedException con messaggio esplicito. - DataConnection/DB/EF/SchemaProviders/FoxProSchemaProvider.cs Implementa IDatabaseSchemaProvider delegando a FoxProReader. Gestione errori per tabella in GetDatabaseSchemaAsync (una tabella non apribile non blocca la discovery). - FOXPRO_CONNECTION.md Guida utente: passo-passo per creare la credenziale, formato connection string, tabella tipi VFP supportati, note su sola lettura e limitazioni query, tabella risoluzione problemi (caratteri accentati, tabelle mancanti, ecc.). ## File modificati - DataConnection/DataConnection.csproj Aggiunto DbfDataReader 1.0.0 (porta transitivamente System.Text.Encoding.CodePages >= 10.0.3; rimossa dipendenza esplicita alla versione 9.0.3 che causava NU1605). - DataConnection/DB/Enums/DatabaseType.cs Aggiunto valore Foxpro in fondo all'enum (preserva valori già persistiti). - CredentialManager/Models/CredentialModels.cs Aggiunto DatabaseType.Foxpro all'enum e metodo BuildFoxproConnectionString che genera "Data Source=percorso[;CodePage=n][;IncludeDeleted=true]". - DataConnection/CredentialManagement/Models/CredentialExtensions.cs Mappatura bidirezionale Foxpro tra enum CredentialManager e DataConnection. - DataConnection/CredentialManagement/Services/DataConnectionCredentialService.cs Aggiunto TestFoxproConnection: verifica percorso accessibile, legge nomi tabelle, restituisce messaggio con conteggio tabelle, encoding rilevato e nota sola lettura. - DataConnection/DB/EF/DatabaseSchemaProviderFactory.cs Caso Foxpro => new FoxProSchemaProvider(). - Data_Coupler/Services/DataConnectionFactory.cs Branch Foxpro => new FoxProDatabaseManager(connectionString). - Data_Coupler/Extensions/DataCoupler/DatabaseMethod.cs Aggiunto IsFoxproConnection() e caso Foxpro in CreateLimitedQuery (SELECT TOP n * FROM (query) AS subquery). - CredentialManager/Integration/DataConnectionHelper.cs ValidateDatabaseCredential: introdotto flag isFileBased (Sqlite || Foxpro) per esonerare host/utente/password/porta; messaggio di errore specifico per il campo percorso FoxPro. - Data_Coupler/Pages/CredentialManagement.razor Aggiunta opzione "Visual FoxPro (.dbc / .dbf)" nel selettore tipo database. Sezione di configurazione dedicata: banner sola lettura, campo percorso con esempi (.dbc e cartella), dropdown code page (Auto/1252/1250/1251/850/437/65001), checkbox record cancellati, pannello tipi supportati, anteprima connection string. Fix validazione form test-connessione: branch Foxpro che verifica solo DatabaseName (non Host/Username/Password), eliminando il falso errore "Il campo Host è obbligatorio". Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
521 lines
20 KiB
C#
521 lines
20 KiB
C#
using System.ComponentModel.DataAnnotations;
|
|
|
|
namespace CredentialManager.Models;
|
|
|
|
/// <summary>
|
|
/// Tipi di credenziali supportate
|
|
/// </summary>
|
|
public enum CredentialType
|
|
{
|
|
Database,
|
|
RestApi,
|
|
OAuth,
|
|
ApiKey,
|
|
BasicAuth
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tipi di servizi REST specifici
|
|
/// </summary>
|
|
public enum RestServiceType
|
|
{
|
|
Generic,
|
|
SapB1ServiceLayer,
|
|
Salesforce
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tipo di flusso OAuth2 per Salesforce
|
|
/// </summary>
|
|
public enum SalesforceGrantType
|
|
{
|
|
/// <summary>
|
|
/// Flusso Username/Password (grant_type=password).
|
|
/// Richiede: ClientId, ClientSecret, Username, Password (+SecurityToken se non IP-trusted).
|
|
/// URL di login: https://login.salesforce.com o https://test.salesforce.com.
|
|
/// </summary>
|
|
Password,
|
|
|
|
/// <summary>
|
|
/// Flusso Client Credentials (grant_type=client_credentials) — server-to-server, senza utente.
|
|
/// Richiede: ClientId, ClientSecret.
|
|
/// URL obbligatorio: My Domain URL (es. https://myorg.my.salesforce.com).
|
|
/// La Connected App deve avere "Enable Client Credentials Flow" attivato e un Integration User assegnato.
|
|
/// </summary>
|
|
ClientCredentials
|
|
}
|
|
|
|
/// <summary>
|
|
/// Tipi di database supportati (allineato con DataConnection.Enums.DatabaseType)
|
|
/// </summary>
|
|
public enum DatabaseType
|
|
{
|
|
SqlServer,
|
|
MySql,
|
|
PostgreSql,
|
|
Oracle,
|
|
Sqlite,
|
|
DB2,
|
|
SapHana,
|
|
Odbc,
|
|
OleDb,
|
|
Foxpro
|
|
}
|
|
|
|
/// <summary>
|
|
/// Modalità di connessione ODBC
|
|
/// </summary>
|
|
public enum OdbcConnectionMode
|
|
{
|
|
/// <summary>
|
|
/// Utilizzo di un DSN (Data Source Name) configurato
|
|
/// </summary>
|
|
Dsn,
|
|
|
|
/// <summary>
|
|
/// Costruzione manuale della connection string
|
|
/// </summary>
|
|
Custom
|
|
}
|
|
|
|
/// <summary>
|
|
/// DTO per le credenziali database - completo per tutti i tipi di DB supportati
|
|
/// </summary>
|
|
public class DatabaseCredential
|
|
{
|
|
[Required(ErrorMessage = "Il nome è obbligatorio")]
|
|
public string Name { get; set; } = string.Empty;
|
|
public DatabaseType DatabaseType { get; set; }
|
|
public string Host { get; set; } = string.Empty;
|
|
public int Port { get; set; }
|
|
public string? DatabaseName { get; set; } = string.Empty; // Ora opzionale
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public string? ConnectionString { get; set; }
|
|
public int CommandTimeout { get; set; } = 30;
|
|
public bool IgnoreSslErrors { get; set; } = false;
|
|
public Dictionary<string, string>? AdditionalParameters { get; set; }
|
|
|
|
// ODBC specific properties
|
|
public string? OdbcDsnName { get; set; } // Nome del DSN ODBC (se utilizzato)
|
|
public OdbcConnectionMode OdbcMode { get; set; } = OdbcConnectionMode.Dsn; // Modalità ODBC (DSN o Custom)
|
|
}
|
|
|
|
/// <summary>
|
|
/// DTO per le credenziali REST API - allineato con RestServiceOptions e esteso per servizi specifici
|
|
/// </summary>
|
|
public class RestApiCredential
|
|
{
|
|
[Required(ErrorMessage = "Il nome è obbligatorio")]
|
|
public string Name { get; set; } = string.Empty;
|
|
public RestServiceType ServiceType { get; set; } = RestServiceType.Generic;
|
|
public string BaseUrl { get; set; } = string.Empty;
|
|
public string? ApiKey { get; set; }
|
|
public string? Username { get; set; }
|
|
public string? Password { get; set; }
|
|
public string? AuthToken { get; set; }
|
|
public string? BearerToken { get; set; }
|
|
public int TimeoutSeconds { get; set; } = 100;
|
|
public bool IgnoreSslErrors { get; set; } = false;
|
|
public Dictionary<string, string>? Headers { get; set; }
|
|
public Dictionary<string, string>? AdditionalParameters { get; set; }
|
|
|
|
// Campi specifici per SAP B1 Service Layer
|
|
public string? CompanyDatabase { get; set; }
|
|
public string? Language { get; set; } = "en-US";
|
|
public string? Version { get; set; } = "v1";
|
|
public bool UseTrustedConnection { get; set; } = false;
|
|
|
|
// Campi specifici per Salesforce
|
|
public string? SecurityToken { get; set; }
|
|
public string? ClientId { get; set; }
|
|
public string? ClientSecret { get; set; }
|
|
public string? ApiVersion { get; set; } = "59.0";
|
|
public bool IsSandbox { get; set; } = false;
|
|
public bool UseSoapApi { get; set; } = false;
|
|
public SalesforceGrantType GrantType { get; set; } = SalesforceGrantType.Password;
|
|
public string? RefreshToken { get; set; }
|
|
public string? AccessToken { get; set; }
|
|
public DateTime? TokenExpiry { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Credenziali specifiche per SAP Business One Service Layer
|
|
/// </summary>
|
|
public class SapB1ServiceLayerCredential
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string ServerUrl { get; set; } = string.Empty; // es: https://server:50000/b1s/v1/
|
|
public string CompanyDatabase { get; set; } = string.Empty; // Database dell'azienda SAP
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public string? Language { get; set; } = "en-US"; // Lingua per la sessione
|
|
public int TimeoutSeconds { get; set; } = 300; // Timeout per le chiamate API
|
|
public bool IgnoreSslErrors { get; set; } = false;
|
|
public string? Version { get; set; } = "v1"; // Versione del Service Layer (v1, v2, etc.)
|
|
public bool UseTrustedConnection { get; set; } = false; // Per autenticazione Windows
|
|
public Dictionary<string, string>? AdditionalHeaders { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Credenziali specifiche per Salesforce
|
|
/// </summary>
|
|
public class SalesforceCredential
|
|
{
|
|
public string Name { get; set; } = string.Empty;
|
|
public string LoginUrl { get; set; } = "https://login.salesforce.com"; // o https://test.salesforce.com per sandbox
|
|
public string Username { get; set; } = string.Empty;
|
|
public string Password { get; set; } = string.Empty;
|
|
public string SecurityToken { get; set; } = string.Empty; // Token di sicurezza Salesforce
|
|
public string? ClientId { get; set; } = string.Empty; // Consumer Key per Connected App
|
|
public string? ClientSecret { get; set; } = string.Empty; // Consumer Secret per Connected App
|
|
public string ApiVersion { get; set; } = "59.0"; // Versione API Salesforce
|
|
public bool IsSandbox { get; set; } = false; // Se è un ambiente sandbox
|
|
public int TimeoutSeconds { get; set; } = 120;
|
|
public bool UseSoapApi { get; set; } = false; // Se usare SOAP invece di REST
|
|
/// <summary>Tipo di flusso OAuth2 da utilizzare. Default: Password (retrocompatibile).</summary>
|
|
public SalesforceGrantType GrantType { get; set; } = SalesforceGrantType.Password;
|
|
public string? RefreshToken { get; set; }
|
|
public string? AccessToken { get; set; }
|
|
public DateTime? TokenExpiry { get; set; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// Factory per creare connection string per i diversi tipi di database
|
|
/// </summary>
|
|
public static class ConnectionStringBuilder
|
|
{
|
|
public static string BuildConnectionString(DatabaseCredential credential)
|
|
{
|
|
if (!string.IsNullOrEmpty(credential.ConnectionString))
|
|
return credential.ConnectionString;
|
|
|
|
return credential.DatabaseType switch
|
|
{
|
|
DatabaseType.SqlServer => BuildSqlServerConnectionString(credential),
|
|
DatabaseType.MySql => BuildMySqlConnectionString(credential),
|
|
DatabaseType.PostgreSql => BuildPostgreSqlConnectionString(credential),
|
|
DatabaseType.Oracle => BuildOracleConnectionString(credential),
|
|
DatabaseType.Sqlite => BuildSqliteConnectionString(credential),
|
|
DatabaseType.DB2 => BuildDb2ConnectionString(credential),
|
|
DatabaseType.SapHana => BuildSapHanaConnectionString(credential),
|
|
DatabaseType.Odbc => BuildOdbcConnectionString(credential),
|
|
DatabaseType.OleDb => BuildOleDbConnectionString(credential),
|
|
DatabaseType.Foxpro => BuildFoxproConnectionString(credential),
|
|
_ => throw new NotSupportedException($"Database type {credential.DatabaseType} not supported")
|
|
};
|
|
} private static string BuildSqlServerConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>();
|
|
|
|
// Gestione speciale per SQL Server locale e named instances
|
|
// Se l'host contiene '\' (instance name) o '(localdb)', non aggiungere la porta
|
|
bool hasInstanceName = credential.Host.Contains('\\') ||
|
|
credential.Host.StartsWith("(localdb)", StringComparison.OrdinalIgnoreCase);
|
|
|
|
if (hasInstanceName)
|
|
{
|
|
// Per named instances e LocalDB, non includere la porta
|
|
builder.Add($"Server={credential.Host}");
|
|
}
|
|
else
|
|
{
|
|
// Per connessioni TCP/IP standard, include host e porta
|
|
// Ma solo se la porta non è la default (1433) per localhost
|
|
if ((credential.Host.Equals("localhost", StringComparison.OrdinalIgnoreCase) ||
|
|
credential.Host == "." ||
|
|
credential.Host == "127.0.0.1") && credential.Port == 1433)
|
|
{
|
|
// Per localhost con porta default, ometti la porta per usare Named Pipes
|
|
builder.Add($"Server={credential.Host}");
|
|
}
|
|
else
|
|
{
|
|
// Per altri casi, usa host,porta
|
|
builder.Add($"Server={credential.Host},{credential.Port}");
|
|
}
|
|
}
|
|
|
|
// Se username è vuoto o è "Integrated", usa Windows Authentication
|
|
if (string.IsNullOrWhiteSpace(credential.Username) ||
|
|
credential.Username.Equals("Integrated", StringComparison.OrdinalIgnoreCase) ||
|
|
credential.Username.Equals("Windows", StringComparison.OrdinalIgnoreCase))
|
|
{
|
|
builder.Add("Integrated Security=True");
|
|
}
|
|
else
|
|
{
|
|
// Usa SQL Server Authentication
|
|
builder.Add($"User Id={credential.Username}");
|
|
builder.Add($"Password={credential.Password}");
|
|
}
|
|
|
|
builder.Add($"Connection Timeout={credential.CommandTimeout}");
|
|
|
|
// Aggiungi Database solo se specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
if (credential.IgnoreSslErrors)
|
|
builder.Add("TrustServerCertificate=True");
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
} private static string BuildMySqlConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Server={credential.Host}",
|
|
$"Port={credential.Port}",
|
|
$"Uid={credential.Username}",
|
|
$"Pwd={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
// Aggiungi Database solo se specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
if (credential.IgnoreSslErrors)
|
|
builder.Add("SslMode=None");
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
} private static string BuildPostgreSqlConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Host={credential.Host}",
|
|
$"Port={credential.Port}",
|
|
$"Username={credential.Username}",
|
|
$"Password={credential.Password}",
|
|
$"Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
// Aggiungi Database solo se specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
if (credential.IgnoreSslErrors)
|
|
builder.Add("SSL Mode=Disable");
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
} private static string BuildOracleConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>();
|
|
|
|
// Per Oracle, il formato cambia se c'è o meno un database specificato
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
{
|
|
builder.Add($"Data Source={credential.Host}:{credential.Port}/{credential.DatabaseName}");
|
|
}
|
|
else
|
|
{
|
|
builder.Add($"Data Source={credential.Host}:{credential.Port}");
|
|
}
|
|
|
|
builder.AddRange(new[]
|
|
{
|
|
$"User Id={credential.Username}",
|
|
$"Password={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
});
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildSqliteConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Data Source={credential.DatabaseName}"
|
|
};
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildDb2ConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Server={credential.Host}:{credential.Port}",
|
|
$"Database={credential.DatabaseName}",
|
|
$"UID={credential.Username}",
|
|
$"PWD={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildSapHanaConnectionString(DatabaseCredential credential)
|
|
{
|
|
var builder = new List<string>
|
|
{
|
|
$"Server={credential.Host}:{credential.Port}",
|
|
$"DatabaseName={credential.DatabaseName}",
|
|
$"UserID={credential.Username}",
|
|
$"Password={credential.Password}",
|
|
$"Connection Timeout={credential.CommandTimeout}"
|
|
};
|
|
|
|
AddAdditionalParameters(builder, credential.AdditionalParameters);
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildOdbcConnectionString(DatabaseCredential credential)
|
|
{
|
|
// Se è già presente una connection string personalizzata, utilizzala
|
|
if (!string.IsNullOrEmpty(credential.ConnectionString))
|
|
return credential.ConnectionString;
|
|
|
|
var builder = new List<string>();
|
|
|
|
// Modalità DSN: usa il DSN configurato
|
|
if (credential.OdbcMode == OdbcConnectionMode.Dsn && !string.IsNullOrEmpty(credential.OdbcDsnName))
|
|
{
|
|
builder.Add($"DSN={credential.OdbcDsnName}");
|
|
|
|
// Aggiungi credenziali se fornite
|
|
if (!string.IsNullOrEmpty(credential.Username))
|
|
builder.Add($"UID={credential.Username}");
|
|
|
|
if (!string.IsNullOrEmpty(credential.Password))
|
|
builder.Add($"PWD={credential.Password}");
|
|
}
|
|
// Modalità Custom: costruisci manualmente la connection string
|
|
else
|
|
{
|
|
// Driver (se specificato nei parametri aggiuntivi)
|
|
if (credential.AdditionalParameters?.ContainsKey("Driver") == true)
|
|
{
|
|
builder.Add($"Driver={{{credential.AdditionalParameters["Driver"]}}}");
|
|
}
|
|
|
|
// Server/Host
|
|
if (!string.IsNullOrEmpty(credential.Host))
|
|
{
|
|
builder.Add($"Server={credential.Host}");
|
|
|
|
// Porta (se diversa da 0)
|
|
if (credential.Port > 0)
|
|
builder.Add($"Port={credential.Port}");
|
|
}
|
|
|
|
// Database
|
|
if (!string.IsNullOrEmpty(credential.DatabaseName))
|
|
builder.Add($"Database={credential.DatabaseName}");
|
|
|
|
// Credenziali
|
|
if (!string.IsNullOrEmpty(credential.Username))
|
|
builder.Add($"UID={credential.Username}");
|
|
|
|
if (!string.IsNullOrEmpty(credential.Password))
|
|
builder.Add($"PWD={credential.Password}");
|
|
}
|
|
|
|
// Timeout
|
|
if (credential.CommandTimeout > 0)
|
|
builder.Add($"Connection Timeout={credential.CommandTimeout}");
|
|
|
|
// Parametri aggiuntivi (escludendo Driver se già aggiunto)
|
|
if (credential.AdditionalParameters != null)
|
|
{
|
|
foreach (var param in credential.AdditionalParameters)
|
|
{
|
|
if (param.Key != "Driver") // Driver già gestito sopra
|
|
builder.Add($"{param.Key}={param.Value}");
|
|
}
|
|
}
|
|
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static string BuildOleDbConnectionString(DatabaseCredential credential)
|
|
{
|
|
// Se è già presente una connection string personalizzata, utilizzala
|
|
if (!string.IsNullOrEmpty(credential.ConnectionString))
|
|
return credential.ConnectionString;
|
|
|
|
var builder = new List<string>();
|
|
|
|
// Provider OLE DB (obbligatorio)
|
|
var provider = credential.AdditionalParameters?.GetValueOrDefault("Provider") ?? "VFPOLEDB.1";
|
|
builder.Add($"Provider={provider}");
|
|
|
|
// Data Source: per VFP e Access è il percorso file/cartella
|
|
// DatabaseName è il campo principale (come per SQLite)
|
|
var dataSource = !string.IsNullOrEmpty(credential.DatabaseName)
|
|
? credential.DatabaseName
|
|
: credential.Host;
|
|
|
|
if (!string.IsNullOrEmpty(dataSource))
|
|
builder.Add($"Data Source={dataSource}");
|
|
|
|
// Credenziali (opzionali per VFP file-based)
|
|
if (!string.IsNullOrEmpty(credential.Username))
|
|
builder.Add($"User ID={credential.Username}");
|
|
|
|
if (!string.IsNullOrEmpty(credential.Password))
|
|
builder.Add($"Password={credential.Password}");
|
|
|
|
// Parametri aggiuntivi specifici (es. Collating Sequence, Exclusive, DELETED per VFP)
|
|
if (credential.AdditionalParameters != null)
|
|
{
|
|
foreach (var param in credential.AdditionalParameters)
|
|
{
|
|
if (param.Key != "Provider") // Provider già gestito sopra
|
|
builder.Add($"{param.Key}={param.Value}");
|
|
}
|
|
}
|
|
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Costruisce la "connection string" per una sorgente Visual FoxPro / dBase.
|
|
/// FoxPro non usa un vero connection string: serve solo il percorso (file .dbc o cartella di .dbf),
|
|
/// salvato in <see cref="DatabaseCredential.DatabaseName"/>. Le opzioni encoding/record cancellati
|
|
/// vengono aggiunte come chiavi CodePage/IncludeDeleted, interpretate da FoxProReader.
|
|
/// </summary>
|
|
private static string BuildFoxproConnectionString(DatabaseCredential credential)
|
|
{
|
|
// Una connection string esplicita (es. incollata dall'utente) ha la precedenza.
|
|
if (!string.IsNullOrEmpty(credential.ConnectionString))
|
|
return credential.ConnectionString;
|
|
|
|
var path = !string.IsNullOrEmpty(credential.DatabaseName)
|
|
? credential.DatabaseName
|
|
: credential.Host;
|
|
|
|
var builder = new List<string> { $"Data Source={path}" };
|
|
|
|
if (credential.AdditionalParameters != null)
|
|
{
|
|
if (credential.AdditionalParameters.TryGetValue("Encoding", out var enc) && !string.IsNullOrWhiteSpace(enc))
|
|
builder.Add($"CodePage={enc}");
|
|
|
|
if (credential.AdditionalParameters.TryGetValue("IncludeDeleted", out var del) &&
|
|
del.Equals("true", StringComparison.OrdinalIgnoreCase))
|
|
builder.Add("IncludeDeleted=true");
|
|
}
|
|
|
|
return string.Join(";", builder);
|
|
}
|
|
|
|
private static void AddAdditionalParameters(List<string> builder, Dictionary<string, string>? additionalParams)
|
|
{
|
|
if (additionalParams != null)
|
|
{
|
|
foreach (var param in additionalParams)
|
|
{
|
|
builder.Add($"{param.Key}={param.Value}");
|
|
}
|
|
}
|
|
}
|
|
}
|