diff --git a/TrafagSalesExporter/Components/Pages/Standorte.razor b/TrafagSalesExporter/Components/Pages/Standorte.razor
index 963fe2f..c82c636 100644
--- a/TrafagSalesExporter/Components/Pages/Standorte.razor
+++ b/TrafagSalesExporter/Components/Pages/Standorte.razor
@@ -36,14 +36,14 @@
@if (_connectionStatus.TryGetValue(context.Id, out var status))
{
-
+
@(status.Success ? "OK" : "Fehler") - @status.Stage
}
else
{
- Nicht getestet
+ Nicht getestet
}
@@ -71,7 +71,7 @@
TSC
Schema
Quellsystem
- Server
+ Host
Aktiv
Aktionen
@@ -80,7 +80,7 @@
@context.TSC
@context.Schema
@context.SourceSystem
- @(context.HanaServer?.Name ?? "-")
+ @GetServerNode(context.HanaServer)
@if (context.IsActive)
{
@@ -132,12 +132,6 @@
@(_editingSite.Id == 0 ? "Standort hinzufügen" : "Standort bearbeiten")
-
- @foreach (var s in _servers)
- {
- @s.Name
- }
-
@@ -148,6 +142,25 @@
}
+
+
+
+ HANA-Verbindung
+
+
+
+
+
+
+
+
+
Abbrechen
@@ -162,6 +175,7 @@
private List _sites = new();
private HanaServer _editingServer = new();
private Site _editingSite = new();
+ private HanaServer _editingSiteServer = new();
private bool _serverDialogVisible;
private bool _siteDialogVisible;
private readonly DialogOptions _dialogOptions = new() { MaxWidth = MaxWidth.Small, FullWidth = true };
@@ -186,19 +200,7 @@
private void EditServer(HanaServer server)
{
- _editingServer = new HanaServer
- {
- Id = server.Id,
- Name = server.Name,
- Host = server.Host,
- Port = server.Port,
- Username = server.Username,
- Password = server.Password,
- DatabaseName = server.DatabaseName,
- UseSsl = server.UseSsl,
- ValidateCertificate = server.ValidateCertificate,
- AdditionalParams = server.AdditionalParams
- };
+ _editingServer = CloneServer(server);
_serverDialogVisible = true;
}
@@ -283,8 +285,9 @@
{
IsActive = true,
SourceSystem = "SAP",
- HanaServerId = _servers.FirstOrDefault()?.Id ?? 0
+ HanaServerId = 0
};
+ _editingSiteServer = CreateDefaultSiteServer();
_siteDialogVisible = true;
}
@@ -300,14 +303,20 @@
SourceSystem = string.IsNullOrWhiteSpace(site.SourceSystem) ? "SAP" : site.SourceSystem,
IsActive = site.IsActive
};
+ _editingSiteServer = site.HanaServer is null
+ ? CreateDefaultSiteServer(site)
+ : CloneServer(site.HanaServer);
_siteDialogVisible = true;
}
private async Task SaveSite()
{
using var db = await DbFactory.CreateDbContextAsync();
+ var serverId = await SaveOrCreateSiteServerAsync(db);
+
if (_editingSite.Id == 0)
{
+ _editingSite.HanaServerId = serverId;
db.Sites.Add(_editingSite);
}
else
@@ -315,7 +324,7 @@
var existing = await db.Sites.FindAsync(_editingSite.Id);
if (existing is not null)
{
- existing.HanaServerId = _editingSite.HanaServerId;
+ existing.HanaServerId = serverId;
existing.Schema = _editingSite.Schema;
existing.TSC = _editingSite.TSC;
existing.Land = _editingSite.Land;
@@ -350,4 +359,93 @@
await LoadDataAsync();
Snackbar.Add("Standort gelöscht", Severity.Info);
}
+
+ private static string GetServerNode(HanaServer? server)
+ {
+ if (server is null || string.IsNullOrWhiteSpace(server.Host))
+ return "-";
+
+ return server.Host.Contains(':', StringComparison.Ordinal) ? server.Host : $"{server.Host}:{server.Port}";
+ }
+
+ private HanaServer CreateDefaultSiteServer(Site? site = null)
+ {
+ var label = !string.IsNullOrWhiteSpace(site?.Land) ? site!.Land : site?.TSC;
+ if (string.IsNullOrWhiteSpace(label))
+ label = "Neuer Standort";
+
+ return new HanaServer
+ {
+ Name = $"{label} HANA",
+ Port = 30015
+ };
+ }
+
+ private static HanaServer CloneServer(HanaServer server)
+ {
+ return new HanaServer
+ {
+ Id = server.Id,
+ Name = server.Name,
+ Host = server.Host,
+ Port = server.Port,
+ Username = server.Username,
+ Password = server.Password,
+ DatabaseName = server.DatabaseName,
+ UseSsl = server.UseSsl,
+ ValidateCertificate = server.ValidateCertificate,
+ AdditionalParams = server.AdditionalParams
+ };
+ }
+
+ private async Task SaveOrCreateSiteServerAsync(AppDbContext db)
+ {
+ _editingSiteServer.Name = string.IsNullOrWhiteSpace(_editingSiteServer.Name)
+ ? $"{_editingSite.Land} HANA".Trim()
+ : _editingSiteServer.Name.Trim();
+ _editingSiteServer.Host = _editingSiteServer.Host.Trim();
+ _editingSiteServer.Username = _editingSiteServer.Username.Trim();
+ _editingSiteServer.DatabaseName = _editingSiteServer.DatabaseName.Trim();
+ _editingSiteServer.AdditionalParams = _editingSiteServer.AdditionalParams.Trim();
+
+ if (string.IsNullOrWhiteSpace(_editingSiteServer.Host))
+ throw new InvalidOperationException("Host oder ServerNode muss gesetzt sein.");
+
+ if (_editingSite.HanaServerId == 0)
+ {
+ db.HanaServers.Add(_editingSiteServer);
+ await db.SaveChangesAsync();
+ return _editingSiteServer.Id;
+ }
+
+ var sharedUseCount = await db.Sites.CountAsync(s => s.HanaServerId == _editingSite.HanaServerId && s.Id != _editingSite.Id);
+ if (sharedUseCount > 0)
+ {
+ var dedicatedServer = CloneServer(_editingSiteServer);
+ dedicatedServer.Id = 0;
+ db.HanaServers.Add(dedicatedServer);
+ await db.SaveChangesAsync();
+ return dedicatedServer.Id;
+ }
+
+ var existingServer = await db.HanaServers.FindAsync(_editingSite.HanaServerId);
+ if (existingServer is null)
+ {
+ db.HanaServers.Add(_editingSiteServer);
+ await db.SaveChangesAsync();
+ return _editingSiteServer.Id;
+ }
+
+ existingServer.Name = _editingSiteServer.Name;
+ existingServer.Host = _editingSiteServer.Host;
+ existingServer.Port = _editingSiteServer.Port;
+ existingServer.Username = _editingSiteServer.Username;
+ existingServer.Password = _editingSiteServer.Password;
+ existingServer.DatabaseName = _editingSiteServer.DatabaseName;
+ existingServer.UseSsl = _editingSiteServer.UseSsl;
+ existingServer.ValidateCertificate = _editingSiteServer.ValidateCertificate;
+ existingServer.AdditionalParams = _editingSiteServer.AdditionalParams;
+ await db.SaveChangesAsync();
+ return existingServer.Id;
+ }
}
diff --git a/TrafagSalesExporter/Components/Pages/Transformations.razor b/TrafagSalesExporter/Components/Pages/Transformations.razor
index 96086a3..5a05580 100644
--- a/TrafagSalesExporter/Components/Pages/Transformations.razor
+++ b/TrafagSalesExporter/Components/Pages/Transformations.razor
@@ -67,7 +67,7 @@
-
diff --git a/TrafagSalesExporter/Models/HanaServer.cs b/TrafagSalesExporter/Models/HanaServer.cs
index 36fd579..535823f 100644
--- a/TrafagSalesExporter/Models/HanaServer.cs
+++ b/TrafagSalesExporter/Models/HanaServer.cs
@@ -1,4 +1,5 @@
using System.ComponentModel.DataAnnotations;
+using System.Data.Common;
namespace TrafagSalesExporter.Models;
@@ -41,26 +42,23 @@ public class HanaServer
public string BuildConnectionString()
{
- var parts = new List
- {
- $"ServerNode={Host}:{Port}",
- $"UserName={Username}",
- $"Password={Password}"
- };
+ var builder = new DbConnectionStringBuilder();
+ builder["ServerNode"] = BuildServerNode();
+ builder["UserName"] = Username.Trim();
+ builder["Password"] = Password;
if (!string.IsNullOrWhiteSpace(DatabaseName))
- parts.Add($"DatabaseName={DatabaseName}");
+ builder["DatabaseName"] = DatabaseName.Trim();
if (UseSsl)
{
- parts.Add("encrypt=true");
- parts.Add($"sslValidateCertificate={(ValidateCertificate ? "true" : "false")}");
+ builder["encrypt"] = true;
+ builder["sslValidateCertificate"] = ValidateCertificate;
}
- if (!string.IsNullOrWhiteSpace(AdditionalParams))
- parts.Add(AdditionalParams.Trim().Trim(';'));
+ AppendAdditionalParams(builder);
- return string.Join(";", parts);
+ return builder.ConnectionString;
}
public string GetConnectionStringPreview()
@@ -80,5 +78,67 @@ public class HanaServer
return copy.BuildConnectionString();
}
+
+ private string BuildServerNode()
+ {
+ var normalizedHost = NormalizeHost(Host);
+ if (string.IsNullOrWhiteSpace(normalizedHost))
+ throw new InvalidOperationException("HANA Host darf nicht leer sein.");
+
+ if (HasExplicitPort(normalizedHost))
+ return normalizedHost;
+
+ return $"{normalizedHost}:{Port}";
+ }
+
+ private static string NormalizeHost(string host)
+ {
+ var value = host.Trim();
+ if (string.IsNullOrWhiteSpace(value))
+ return string.Empty;
+
+ if (Uri.TryCreate(value, UriKind.Absolute, out var uri))
+ {
+ return uri.IsDefaultPort ? uri.Host : $"{uri.Host}:{uri.Port}";
+ }
+
+ var schemeIndex = value.IndexOf("://", StringComparison.Ordinal);
+ if (schemeIndex >= 0)
+ value = value[(schemeIndex + 3)..];
+
+ var slashIndex = value.IndexOf('/');
+ if (slashIndex >= 0)
+ value = value[..slashIndex];
+
+ return value.Trim();
+ }
+
+ private static bool HasExplicitPort(string host)
+ {
+ if (host.StartsWith('['))
+ return host.Contains("]:", StringComparison.Ordinal);
+
+ return host.Count(c => c == ':') == 1;
+ }
+
+ private void AppendAdditionalParams(DbConnectionStringBuilder builder)
+ {
+ if (string.IsNullOrWhiteSpace(AdditionalParams))
+ return;
+
+ foreach (var rawPart in AdditionalParams.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries))
+ {
+ var separatorIndex = rawPart.IndexOf('=');
+ if (separatorIndex <= 0 || separatorIndex == rawPart.Length - 1)
+ continue;
+
+ var key = rawPart[..separatorIndex].Trim();
+ var value = rawPart[(separatorIndex + 1)..].Trim();
+ if (key.Length == 0)
+ continue;
+
+ builder[key] = value;
+ }
+ }
}