From f916c26fb448b7d66fa43472e15e0623c0756077 Mon Sep 17 00:00:00 2001 From: Claude Date: Fri, 10 Apr 2026 06:23:59 +0000 Subject: [PATCH 1/2] Fix SAP HANA client reference - use direct DLL reference instead of missing NuGet package Das Paket 'Sap.Data.Hana.v2' existiert nicht auf nuget.org. SAP liefert den HANA .NET Client ausschliesslich ueber das SAP HANA Client Installationspaket aus. Stattdessen wird nun Sap.Data.Hana.Core.v2.1.dll direkt aus dem Standard-Installationspfad referenziert (via HanaClientDll MSBuild-Property ueberschreibbar). Warnung beim Build wenn DLL nicht gefunden wird. https://claude.ai/code/session_012heAXNMbbyxqYf2S2HrKLj --- .../TrafagSalesExporter.csproj | 21 ++++++++++++++++++- 1 file changed, 20 insertions(+), 1 deletion(-) diff --git a/TrafagSalesExporter/TrafagSalesExporter.csproj b/TrafagSalesExporter/TrafagSalesExporter.csproj index ddd1092..c60dfbc 100644 --- a/TrafagSalesExporter/TrafagSalesExporter.csproj +++ b/TrafagSalesExporter/TrafagSalesExporter.csproj @@ -3,6 +3,14 @@ net8.0 enable enable + + + C:\Program Files\sap\hdbclient\dotnetcore\v2.1\Sap.Data.Hana.Core.v2.1.dll @@ -15,6 +23,17 @@ - + + + + $(HanaClientDll) + true + + + + + + From efcf7b180cc0553f860bb63ffedf83896fdd277e Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 13 Apr 2026 08:52:10 +0000 Subject: [PATCH 2/2] 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(); }