From efcf7b180cc0553f860bb63ffedf83896fdd277e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 08:52:10 +0000 Subject: [PATCH] Add SSL, MDC database, and custom HANA connection parameters Fixes 'error while parsing protocol' HanaException by supporting SSL/TLS encryption, Multi-Tenant Database Container (MDC) database name, and arbitrary additional connection parameters. - HanaServer model: added DatabaseName, UseSsl, ValidateCertificate, AdditionalParams fields + BuildConnectionString() helper - HanaQueryService: accepts HanaServer directly, uses BuildConnectionString() for full parameter support - AppDbContext: added EnsureSchema() method that uses PRAGMA table_info + ALTER TABLE ADD COLUMN to add the new fields to existing SQLite databases without losing data (EnsureCreated does not update schema) - Program.cs: calls EnsureSchema on startup before seeding - Standorte.razor: server dialog now exposes DatabaseName, UseSsl, ValidateCertificate, AdditionalParams with helper texts; test connection uses new signature https://claude.ai/code/session_012heAXNMbbyxqYf2S2HrKLj --- .../Components/Pages/Standorte.razor | 25 +++++++++-- TrafagSalesExporter/Data/AppDbContext.cs | 41 +++++++++++++++++ TrafagSalesExporter/Models/HanaServer.cs | 45 +++++++++++++++++++ TrafagSalesExporter/Program.cs | 1 + .../Services/ExportOrchestrationService.cs | 4 +- .../Services/HanaQueryService.cs | 8 ++-- 6 files changed, 113 insertions(+), 11 deletions(-) diff --git a/TrafagSalesExporter/Components/Pages/Standorte.razor b/TrafagSalesExporter/Components/Pages/Standorte.razor index b524454..2495b8e 100644 --- a/TrafagSalesExporter/Components/Pages/Standorte.razor +++ b/TrafagSalesExporter/Components/Pages/Standorte.razor @@ -93,10 +93,19 @@ - - + + + + + + Abbrechen @@ -164,7 +173,11 @@ Host = server.Host, Port = server.Port, Username = server.Username, - Password = server.Password + Password = server.Password, + DatabaseName = server.DatabaseName, + UseSsl = server.UseSsl, + ValidateCertificate = server.ValidateCertificate, + AdditionalParams = server.AdditionalParams }; _serverDialogVisible = true; } @@ -186,6 +199,10 @@ existing.Port = _editingServer.Port; existing.Username = _editingServer.Username; existing.Password = _editingServer.Password; + existing.DatabaseName = _editingServer.DatabaseName; + existing.UseSsl = _editingServer.UseSsl; + existing.ValidateCertificate = _editingServer.ValidateCertificate; + existing.AdditionalParams = _editingServer.AdditionalParams; } } await db.SaveChangesAsync(); @@ -218,7 +235,7 @@ { try { - await Task.Run(() => HanaService.TestConnection(server.Host, server.Port, server.Username, server.Password)); + await Task.Run(() => HanaService.TestConnection(server)); Snackbar.Add($"Verbindung zu '{server.Name}' erfolgreich!", Severity.Success); } catch (Exception ex) diff --git a/TrafagSalesExporter/Data/AppDbContext.cs b/TrafagSalesExporter/Data/AppDbContext.cs index c045206..6f2decd 100644 --- a/TrafagSalesExporter/Data/AppDbContext.cs +++ b/TrafagSalesExporter/Data/AppDbContext.cs @@ -1,3 +1,4 @@ +using System.Data; using Microsoft.EntityFrameworkCore; using TrafagSalesExporter.Models; @@ -13,6 +14,46 @@ public class AppDbContext : DbContext public DbSet ExportSettings => Set(); public DbSet ExportLogs => Set(); + /// + /// Fügt Spalten zu existierenden Tabellen hinzu, die bei neueren Versionen + /// hinzugekommen sind. EnsureCreated aktualisiert das Schema nicht automatisch. + /// + public static void EnsureSchema(AppDbContext db) + { + AddColumnIfMissing(db, "HanaServers", "DatabaseName", "TEXT NOT NULL DEFAULT ''"); + AddColumnIfMissing(db, "HanaServers", "UseSsl", "INTEGER NOT NULL DEFAULT 0"); + AddColumnIfMissing(db, "HanaServers", "ValidateCertificate", "INTEGER NOT NULL DEFAULT 0"); + AddColumnIfMissing(db, "HanaServers", "AdditionalParams", "TEXT NOT NULL DEFAULT ''"); + } + + private static void AddColumnIfMissing(AppDbContext db, string table, string column, string type) + { + var conn = db.Database.GetDbConnection(); + if (conn.State != ConnectionState.Open) conn.Open(); + + bool exists = false; + using (var cmd = conn.CreateCommand()) + { + cmd.CommandText = $"PRAGMA table_info({table})"; + using var reader = cmd.ExecuteReader(); + while (reader.Read()) + { + if (string.Equals(reader["name"]?.ToString(), column, StringComparison.OrdinalIgnoreCase)) + { + exists = true; + break; + } + } + } + + if (!exists) + { + using var alter = conn.CreateCommand(); + alter.CommandText = $"ALTER TABLE {table} ADD COLUMN {column} {type}"; + alter.ExecuteNonQuery(); + } + } + public static void SeedIfEmpty(AppDbContext db) { if (db.HanaServers.Any()) return; diff --git a/TrafagSalesExporter/Models/HanaServer.cs b/TrafagSalesExporter/Models/HanaServer.cs index 157953d..a77b146 100644 --- a/TrafagSalesExporter/Models/HanaServer.cs +++ b/TrafagSalesExporter/Models/HanaServer.cs @@ -17,4 +17,49 @@ public class HanaServer public string Username { get; set; } = string.Empty; public string Password { get; set; } = string.Empty; + + /// + /// Name der Tenant-Datenbank bei Multi-Tenant Database Container (MDC) Setups. + /// Leer lassen, wenn direkt auf einen Tenant-Port verbunden wird. + /// + public string DatabaseName { get; set; } = string.Empty; + + /// + /// SSL/TLS Verschlüsselung aktivieren (encrypt=true). + /// + public bool UseSsl { get; set; } + + /// + /// SSL-Zertifikat validieren. Bei self-signed Zertifikaten auf false setzen. + /// + public bool ValidateCertificate { get; set; } + + /// + /// Zusätzliche Verbindungsparameter (Semikolon-getrennt), z.B. "sslCryptoProvider=openssl". + /// + public string AdditionalParams { get; set; } = string.Empty; + + public string BuildConnectionString() + { + var parts = new List + { + $"ServerNode={Host}:{Port}", + $"UserName={Username}", + $"Password={Password}" + }; + + if (!string.IsNullOrWhiteSpace(DatabaseName)) + parts.Add($"DatabaseName={DatabaseName}"); + + if (UseSsl) + { + parts.Add("encrypt=true"); + parts.Add($"sslValidateCertificate={(ValidateCertificate ? "true" : "false")}"); + } + + if (!string.IsNullOrWhiteSpace(AdditionalParams)) + parts.Add(AdditionalParams.Trim().Trim(';')); + + return string.Join(";", parts); + } } diff --git a/TrafagSalesExporter/Program.cs b/TrafagSalesExporter/Program.cs index 054df35..609affa 100644 --- a/TrafagSalesExporter/Program.cs +++ b/TrafagSalesExporter/Program.cs @@ -27,6 +27,7 @@ using (var scope = app.Services.CreateScope()) var dbFactory = scope.ServiceProvider.GetRequiredService>(); using var db = await dbFactory.CreateDbContextAsync(); await db.Database.EnsureCreatedAsync(); + AppDbContext.EnsureSchema(db); AppDbContext.SeedIfEmpty(db); } diff --git a/TrafagSalesExporter/Services/ExportOrchestrationService.cs b/TrafagSalesExporter/Services/ExportOrchestrationService.cs index 6fddbb5..ae13bd1 100644 --- a/TrafagSalesExporter/Services/ExportOrchestrationService.cs +++ b/TrafagSalesExporter/Services/ExportOrchestrationService.cs @@ -94,9 +94,7 @@ public class ExportOrchestrationService UpdateStatus(site.Id, "HANA Abfrage..."); var records = await Task.Run(() => _hanaService.GetSalesRecords( - site.HanaServer.Host, site.HanaServer.Port, - site.HanaServer.Username, site.HanaServer.Password, - site.Schema, site.TSC, site.Land, settings.DateFilter)); + site.HanaServer, site.Schema, site.TSC, site.Land, settings.DateFilter)); UpdateStatus(site.Id, "Excel erstellen..."); var outputDir = Path.Combine(AppContext.BaseDirectory, "output"); diff --git a/TrafagSalesExporter/Services/HanaQueryService.cs b/TrafagSalesExporter/Services/HanaQueryService.cs index 8872d2e..4c2a9e1 100644 --- a/TrafagSalesExporter/Services/HanaQueryService.cs +++ b/TrafagSalesExporter/Services/HanaQueryService.cs @@ -5,10 +5,10 @@ namespace TrafagSalesExporter.Services; public class HanaQueryService { - public List GetSalesRecords(string host, int port, string username, string password, + public List GetSalesRecords(HanaServer server, string schema, string tsc, string land, string dateFilter) { - var connectionString = $"ServerNode={host}:{port};UserName={username};Password={password}"; + var connectionString = server.BuildConnectionString(); var result = new List(); using var connection = new HanaConnection(connectionString); @@ -32,9 +32,9 @@ public class HanaQueryService return result; } - public void TestConnection(string host, int port, string username, string password) + public void TestConnection(HanaServer server) { - var connectionString = $"ServerNode={host}:{port};UserName={username};Password={password}"; + var connectionString = server.BuildConnectionString(); using var connection = new HanaConnection(connectionString); connection.Open(); }