Seperate database entity from ui object (Reactive)

This commit is contained in:
eelke 2025-08-31 14:25:27 +02:00
parent 739d6bd65a
commit 747358297b
10 changed files with 176 additions and 154 deletions

View file

@ -8,8 +8,6 @@ namespace pgLabII.PgUtils.ConnectionStrings;
/// </summary> /// </summary>
public sealed class ConnectionDescriptor public sealed class ConnectionDescriptor
{ {
public string? Name { get; init; }
// Primary hosts (support multi-host). If empty, implies localhost default. // Primary hosts (support multi-host). If empty, implies localhost default.
public IReadOnlyList<HostEndpoint> Hosts { get; init; } = new List<HostEndpoint>(); public IReadOnlyList<HostEndpoint> Hosts { get; init; } = new List<HostEndpoint>();

View file

@ -12,20 +12,19 @@ public class ServerConfigurationMappingTests
[Fact] [Fact]
public void ToDescriptor_Basic_MapsExpectedFields() public void ToDescriptor_Basic_MapsExpectedFields()
{ {
var cfg = new ServerConfiguration ServerConfigurationEntity cfg = new()
{ {
Name = "Prod", Name = "Prod",
Host = "db.example.com", Host = "db.example.com",
Port = 5433, Port = 5433,
InitialDatabase = "appdb", InitialDatabase = "appdb",
DefaultSslMode = SslMode.Require, SslMode = SslMode.Require,
User = new ServerUser { Name = "alice", Password = "secret" } User = new ServerUser { Name = "alice", Password = "secret" }
}; };
var extra = new Dictionary<string,string>{{"search_path","public"}}; var extra = new Dictionary<string,string>{{"search_path","public"}};
var d = ServerConfigurationMapping.ToDescriptor(cfg, applicationName: "pgLabII", timeoutSeconds: 15, extraProperties: extra); var d = ServerConfigurationMapping.ToDescriptor(cfg, applicationName: "pgLabII", timeoutSeconds: 15, extraProperties: extra);
Assert.Equal("Prod", d.Name);
Assert.Single(d.Hosts); Assert.Single(d.Hosts);
Assert.Equal("db.example.com", d.Hosts[0].Host); Assert.Equal("db.example.com", d.Hosts[0].Host);
Assert.Equal((ushort)5433, d.Hosts[0].Port); Assert.Equal((ushort)5433, d.Hosts[0].Port);
@ -42,7 +41,7 @@ public class ServerConfigurationMappingTests
[Fact] [Fact]
public void ToDescriptor_OmitsEmptyFields() public void ToDescriptor_OmitsEmptyFields()
{ {
var cfg = new ServerConfiguration ServerConfigurationEntity cfg = new ()
{ {
Name = "Empty", Name = "Empty",
Host = "", Host = "",
@ -63,7 +62,6 @@ public class ServerConfigurationMappingTests
{ {
var desc = new ConnectionDescriptor var desc = new ConnectionDescriptor
{ {
Name = "Staging",
Hosts = new [] Hosts = new []
{ {
new HostEndpoint{ Host = "host1", Port = 5432 }, new HostEndpoint{ Host = "host1", Port = 5432 },
@ -75,13 +73,12 @@ public class ServerConfigurationMappingTests
SslMode = SslMode.VerifyFull SslMode = SslMode.VerifyFull
}; };
var cfg = ServerConfigurationMapping.FromDescriptor(desc); ServerConfigurationEntity cfg = ServerConfigurationMapping.FromDescriptor(desc);
Assert.Equal("Staging", cfg.Name);
Assert.Equal("host1", cfg.Host); Assert.Equal("host1", cfg.Host);
Assert.Equal((ushort)5432, cfg.Port); Assert.Equal((ushort)5432, cfg.Port);
Assert.Equal("stagedb", cfg.InitialDatabase); Assert.Equal("stagedb", cfg.InitialDatabase);
Assert.Equal(SslMode.VerifyFull, cfg.DefaultSslMode); Assert.Equal(SslMode.VerifyFull, cfg.SslMode);
Assert.Equal("bob", cfg.User.Name); Assert.Equal("bob", cfg.User.Name);
Assert.Equal("pwd", cfg.User.Password); Assert.Equal("pwd", cfg.User.Password);
} }
@ -89,13 +86,13 @@ public class ServerConfigurationMappingTests
[Fact] [Fact]
public void FromDescriptor_UpdatesExisting_PreservesMissing() public void FromDescriptor_UpdatesExisting_PreservesMissing()
{ {
var existing = new ServerConfiguration ServerConfigurationEntity existing = new()
{ {
Name = "Existing", Name = "Existing",
Host = "keep-host", Host = "keep-host",
Port = 5432, Port = 5432,
InitialDatabase = "keepdb", InitialDatabase = "keepdb",
DefaultSslMode = SslMode.Prefer, SslMode = SslMode.Prefer,
User = new ServerUser { Name = "keepuser", Password = "keeppwd" } User = new ServerUser { Name = "keepuser", Password = "keeppwd" }
}; };
@ -110,7 +107,7 @@ public class ServerConfigurationMappingTests
Assert.Equal("new-host", cfg.Host); Assert.Equal("new-host", cfg.Host);
Assert.Equal((ushort)5432, cfg.Port); // unchanged Assert.Equal((ushort)5432, cfg.Port); // unchanged
Assert.Equal("keepdb", cfg.InitialDatabase); // preserved Assert.Equal("keepdb", cfg.InitialDatabase); // preserved
Assert.Equal(SslMode.Prefer, cfg.DefaultSslMode); // preserved Assert.Equal(SslMode.Prefer, cfg.SslMode); // preserved
Assert.Equal("keepuser", cfg.User.Name); // preserved Assert.Equal("keepuser", cfg.User.Name); // preserved
Assert.Equal("keeppwd", cfg.User.Password); // preserved Assert.Equal("keeppwd", cfg.User.Password); // preserved
} }
@ -118,24 +115,23 @@ public class ServerConfigurationMappingTests
[Fact] [Fact]
public void Roundtrip_Basic() public void Roundtrip_Basic()
{ {
var cfg = new ServerConfiguration ServerConfigurationEntity cfg = new()
{ {
Name = "Round", Name = "Round",
Host = "localhost", Host = "localhost",
Port = 5432, Port = 5432,
InitialDatabase = "postgres", InitialDatabase = "postgres",
DefaultSslMode = SslMode.Allow, SslMode = SslMode.Allow,
User = new ServerUser { Name = "me", Password = "pw" } User = new ServerUser { Name = "me", Password = "pw" }
}; };
var d = ServerConfigurationMapping.ToDescriptor(cfg); var d = ServerConfigurationMapping.ToDescriptor(cfg);
var cfg2 = ServerConfigurationMapping.FromDescriptor(d); var cfg2 = ServerConfigurationMapping.FromDescriptor(d);
Assert.Equal(cfg.Name, cfg2.Name);
Assert.Equal(cfg.Host, cfg2.Host); Assert.Equal(cfg.Host, cfg2.Host);
Assert.Equal(cfg.Port, cfg2.Port); Assert.Equal(cfg.Port, cfg2.Port);
Assert.Equal(cfg.InitialDatabase, cfg2.InitialDatabase); Assert.Equal(cfg.InitialDatabase, cfg2.InitialDatabase);
Assert.Equal(cfg.DefaultSslMode, cfg2.DefaultSslMode); Assert.Equal(cfg.SslMode, cfg2.SslMode);
Assert.Equal(cfg.User.Name, cfg2.User.Name); Assert.Equal(cfg.User.Name, cfg2.User.Name);
Assert.Equal(cfg.User.Password, cfg2.User.Password); Assert.Equal(cfg.User.Password, cfg2.User.Password);
} }

View file

@ -13,7 +13,7 @@ public class EditServerConfigurationWindowTests
public void Parse_and_Generate_roundtrip_via_UI_bindings() public void Parse_and_Generate_roundtrip_via_UI_bindings()
{ {
// Arrange: initialize Avalonia headless app once for the test // Arrange: initialize Avalonia headless app once for the test
var vm = new EditServerConfigurationViewModel(new ServerConfiguration()); var vm = new EditServerConfigurationViewModel(new ServerConfigurationEntity());
var window = new EditServerConfigurationWindow(vm); var window = new EditServerConfigurationWindow(vm);
// Act: set an URL input, auto mode, then parse // Act: set an URL input, auto mode, then parse
@ -46,7 +46,7 @@ public class EditServerConfigurationWindowTests
[AvaloniaFact] [AvaloniaFact]
public void Forced_format_overrides_auto_detection() public void Forced_format_overrides_auto_detection()
{ {
var vm = new EditServerConfigurationViewModel(new ServerConfiguration()); var vm = new EditServerConfigurationViewModel(new ServerConfigurationEntity());
// Use a string with quoted values that libpq would struggle with due to incorrect quoting // Use a string with quoted values that libpq would struggle with due to incorrect quoting
vm.InputConnectionString = "Host=\"server with spaces\";Username=\"bob\";Password=\"secret\";Database=\"db1\""; vm.InputConnectionString = "Host=\"server with spaces\";Username=\"bob\";Password=\"secret\";Database=\"db1\"";
@ -69,7 +69,7 @@ public class EditServerConfigurationWindowTests
[AvaloniaFact] [AvaloniaFact]
public void Parse_Npgsql_with_inline_host_port_updates_all_fields() public void Parse_Npgsql_with_inline_host_port_updates_all_fields()
{ {
var vm = new EditServerConfigurationViewModel(new ServerConfiguration()); var vm = new EditServerConfigurationViewModel(new ServerConfigurationEntity());
vm.InputConnectionString = "Host=host.docker.internal:5432;Database=kms_quartz;Username=postgres;Password=admin;Trust Server Certificate=true"; vm.InputConnectionString = "Host=host.docker.internal:5432;Database=kms_quartz;Username=postgres;Password=admin;Trust Server Certificate=true";
vm.ForcedFormat = EditServerConfigurationViewModel.ForcedFormatOption.Auto; vm.ForcedFormat = EditServerConfigurationViewModel.ForcedFormatOption.Auto;
vm.ParseConnectionStringCommand.Execute().Subscribe(); vm.ParseConnectionStringCommand.Execute().Subscribe();

View file

@ -8,7 +8,7 @@ namespace pgLabII.Infra;
public class LocalDb : DbContext public class LocalDb : DbContext
{ {
public DbSet<ServerConfiguration> ServerConfigurations => Set<ServerConfiguration>(); public DbSet<ServerConfigurationEntity> ServerConfigurations => Set<ServerConfigurationEntity>();
public DbSet<Document> Documents => Set<Document>(); public DbSet<Document> Documents => Set<Document>();
public DbSet<EditHistoryEntry> EditHistory => Set<EditHistoryEntry>(); public DbSet<EditHistoryEntry> EditHistory => Set<EditHistoryEntry>();
@ -30,7 +30,7 @@ public class LocalDb : DbContext
protected override void OnModelCreating(ModelBuilder modelBuilder) protected override void OnModelCreating(ModelBuilder modelBuilder)
{ {
new ServerConfigurationEntityConfiguration().Configure(modelBuilder.Entity<ServerConfiguration>()); new ServerConfigurationEntityConfiguration().Configure(modelBuilder.Entity<ServerConfigurationEntity>());
new ServerUserEntityConfiguration().Configure(modelBuilder.Entity<ServerUser>()); new ServerUserEntityConfiguration().Configure(modelBuilder.Entity<ServerUser>());
new DocumentEntityConfiguration().Configure(modelBuilder.Entity<Document>()); new DocumentEntityConfiguration().Configure(modelBuilder.Entity<Document>());
new EditHistoryEntityConfiguration().Configure(modelBuilder.Entity<EditHistoryEntry>()); new EditHistoryEntityConfiguration().Configure(modelBuilder.Entity<EditHistoryEntry>());
@ -40,15 +40,16 @@ public class LocalDb : DbContext
{ {
base.ConfigureConventions(configurationBuilder); base.ConfigureConventions(configurationBuilder);
// Keep Color converter for any other entities still using Avalonia Color
configurationBuilder configurationBuilder
.Properties<Color>() .Properties<Color>()
.HaveConversion<ColorConverter>(); .HaveConversion<ColorConverter>();
} }
} }
public class ServerConfigurationEntityConfiguration : IEntityTypeConfiguration<ServerConfiguration> public class ServerConfigurationEntityConfiguration : IEntityTypeConfiguration<ServerConfigurationEntity>
{ {
public void Configure(EntityTypeBuilder<ServerConfiguration> b) public void Configure(EntityTypeBuilder<ServerConfigurationEntity> b)
{ {
b.HasKey(e => e.Id); b.HasKey(e => e.Id);
} }

View file

@ -1,103 +0,0 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics;
using System.Reactive;
using Avalonia.Media;
using Npgsql;
using pgLabII.ViewModels;
using pgLabII.Views;
using ReactiveUI;
using ReactiveUI.SourceGenerators;
namespace pgLabII.Model;
public partial class ServerConfiguration : ReactiveObject
{
private Color _color;
private bool _colorEnabled = true;
[Reactive] private Guid _id = Guid.NewGuid();
/// <summary>
/// For the user to help him identify the item
/// </summary>
[Reactive] private string _name = "";
public Color Color
{
get => _color;
set
{
if (_color != value)
{
_color = value;
this.RaisePropertyChanged();
this.RaisePropertyChanged(propertyName: nameof(BackgroundBrush));
}
}
}
public bool ColorEnabled
{
get => _colorEnabled;
set
{
if (_colorEnabled != value)
{
_colorEnabled = value;
this.RaisePropertyChanged();
this.RaisePropertyChanged(propertyName: nameof(BackgroundBrush));
}
}
}
[Reactive] private string _host = "";
[Reactive] private ushort _port = 5432;
[Reactive] private string _initialDatabase = "";
[Reactive] private SslMode _sslMode = SslMode.Prefer;
// Explicit property wrapper to make compiled XAML binding findable
public SslMode DefaultSslMode
{
get => _sslMode;
set => this.RaiseAndSetIfChanged(ref _sslMode, value);
}
public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null;
public ServerUser User { get; set; } = new();
public ReactiveCommand<Unit, Unit> EditCommand { get; }
public ReactiveCommand<Unit, Unit> ExploreCommand { get; }
public ServerConfiguration()
{
EditCommand = ReactiveCommand.Create(() =>
{
EditServerConfigurationWindow window = new(
new ViewModels.EditServerConfigurationViewModel(this))
{ New = false };
window.Show();
});
ExploreCommand = ReactiveCommand.Create(() =>
{
SingleDatabaseWindow window = new() { DataContext = new ViewListViewModel() };
window.Show();
});
}
public ServerConfiguration(ServerConfiguration src)
: this()
{
Color = src.Color;
ColorEnabled = src.ColorEnabled;
Id = src.Id;
Name = src.Name;
Port = src.Port;
InitialDatabase = src.InitialDatabase;
DefaultSslMode = src.DefaultSslMode;
User = src.User;
}
}

View file

@ -0,0 +1,20 @@
using System;
using Npgsql;
namespace pgLabII.Model;
// Pure persistence entity for EF Core: no UI dependencies, no ReactiveObject
public class ServerConfigurationEntity
{
public Guid Id { get; set; } = Guid.NewGuid();
public string Name { get; set; } = string.Empty;
public string Host { get; set; } = string.Empty;
public ushort Port { get; set; } = 5432;
public string InitialDatabase { get; set; } = string.Empty;
public SslMode SslMode { get; set; } = SslMode.Prefer;
public bool ColorEnabled { get; set; } = true;
public int ColorArgb { get; set; } = unchecked((int)0xFF_33_33_33); // default dark gray
public Guid UserId { get; set; }
public ServerUser User { get; set; } = new();
}

View file

@ -21,7 +21,7 @@ public static class ServerConfigurationMapping
/// - ApplicationName and TimeoutSeconds don't exist on ServerConfiguration; we preserve any passed-in /// - ApplicationName and TimeoutSeconds don't exist on ServerConfiguration; we preserve any passed-in
/// values via optional parameters or Properties if provided by caller. /// values via optional parameters or Properties if provided by caller.
/// </summary> /// </summary>
public static ConnectionDescriptor ToDescriptor(ServerConfiguration cfg, public static ConnectionDescriptor ToDescriptor(ServerConfigurationEntity cfg,
string? applicationName = null, string? applicationName = null,
int? timeoutSeconds = null, int? timeoutSeconds = null,
IReadOnlyDictionary<string, string>? extraProperties = null) IReadOnlyDictionary<string, string>? extraProperties = null)
@ -43,12 +43,11 @@ public static class ServerConfigurationMapping
return new ConnectionDescriptor return new ConnectionDescriptor
{ {
Name = cfg.Name,
Hosts = hosts, Hosts = hosts,
Database = string.IsNullOrWhiteSpace(cfg.InitialDatabase) ? null : cfg.InitialDatabase, Database = string.IsNullOrWhiteSpace(cfg.InitialDatabase) ? null : cfg.InitialDatabase,
Username = string.IsNullOrWhiteSpace(cfg.User?.Name) ? null : cfg.User!.Name, Username = string.IsNullOrWhiteSpace(cfg.User?.Name) ? null : cfg.User!.Name,
Password = string.IsNullOrEmpty(cfg.User?.Password) ? null : cfg.User!.Password, Password = string.IsNullOrEmpty(cfg.User?.Password) ? null : cfg.User!.Password,
SslMode = cfg.DefaultSslMode, SslMode = cfg.SslMode,
ApplicationName = applicationName, ApplicationName = applicationName,
TimeoutSeconds = timeoutSeconds, TimeoutSeconds = timeoutSeconds,
Properties = props Properties = props
@ -61,17 +60,13 @@ public static class ServerConfigurationMapping
/// - If descriptor has multiple hosts, the first is mapped to Host/Port. /// - If descriptor has multiple hosts, the first is mapped to Host/Port.
/// - If descriptor omits sslmode/database/username/password, existing values are preserved (if any). /// - If descriptor omits sslmode/database/username/password, existing values are preserved (if any).
/// </summary> /// </summary>
public static ServerConfiguration FromDescriptor(ConnectionDescriptor descriptor, ServerConfiguration? existing = null) public static ServerConfigurationEntity FromDescriptor(ConnectionDescriptor descriptor, ServerConfigurationEntity? existing = null)
{ {
if (descriptor == null) throw new ArgumentNullException(nameof(descriptor)); if (descriptor == null) throw new ArgumentNullException(nameof(descriptor));
var cfg = existing ?? new ServerConfiguration(); var cfg = existing ?? new ServerConfigurationEntity();
// Name
if (!string.IsNullOrWhiteSpace(descriptor.Name))
cfg.Name = descriptor.Name!;
// Host/Port: take first // Host/Port: take first
if (descriptor.Hosts != null && descriptor.Hosts.Count > 0) if (descriptor.Hosts.Count > 0)
{ {
var h = descriptor.Hosts[0]; var h = descriptor.Hosts[0];
if (!string.IsNullOrWhiteSpace(h.Host)) if (!string.IsNullOrWhiteSpace(h.Host))
@ -86,9 +81,10 @@ public static class ServerConfigurationMapping
// SSL Mode // SSL Mode
if (descriptor.SslMode.HasValue) if (descriptor.SslMode.HasValue)
cfg.DefaultSslMode = descriptor.SslMode.Value; cfg.SslMode = descriptor.SslMode.Value;
// User // User
// Suspect, should the user object be replaced instead of updated?
if (cfg.User == null) if (cfg.User == null)
cfg.User = new ServerUser(); cfg.User = new ServerUser();
if (!string.IsNullOrWhiteSpace(descriptor.Username)) if (!string.IsNullOrWhiteSpace(descriptor.Username))
@ -99,4 +95,26 @@ public static class ServerConfigurationMapping
// Nothing to do for ApplicationName/TimeoutSeconds here; not represented in ServerConfiguration. // Nothing to do for ApplicationName/TimeoutSeconds here; not represented in ServerConfiguration.
return cfg; return cfg;
} }
// Overloads for new UI ViewModel wrapper
public static ConnectionDescriptor ToDescriptor(pgLabII.ViewModels.ServerConfigurationViewModel cfgVm,
string? applicationName = null,
int? timeoutSeconds = null,
IReadOnlyDictionary<string, string>? extraProperties = null)
=> ToDescriptor(cfgVm.Entity, applicationName, timeoutSeconds, extraProperties);
public static void FromDescriptorInto(pgLabII.ViewModels.ServerConfigurationViewModel targetVm, ConnectionDescriptor descriptor)
{
//var updated = targetVm.Entity;
var n = FromDescriptor(descriptor, null);
// push back updated values into VM's entity to trigger the notifies
targetVm.Host = n.Host;
targetVm.Port = n.Port;
targetVm.InitialDatabase = n.InitialDatabase;
targetVm.DefaultSslMode = n.SslMode;
// Suspect, if we share Users between configurations then we should not be overriding this struct
if (targetVm.User == null) targetVm.User = new ServerUser();
targetVm.User.Name = n.User?.Name ?? string.Empty;
targetVm.User.Password = n.User?.Password ?? string.Empty;
}
} }

View file

@ -12,7 +12,8 @@ namespace pgLabII.ViewModels;
public class EditServerConfigurationViewModel : ViewModelBase public class EditServerConfigurationViewModel : ViewModelBase
{ {
public ServerConfiguration Configuration { get; set; } // Prefer new UI VM; keep old model for compatibility by wrapping when needed
public ServerConfigurationViewModel Configuration { get; set; }
// Connection string IO // Connection string IO
private string _inputConnectionString = string.Empty; private string _inputConnectionString = string.Empty;
@ -86,7 +87,7 @@ public class EditServerConfigurationViewModel : ViewModelBase
public EditServerConfigurationViewModel() public EditServerConfigurationViewModel()
{ {
Configuration = new(); Configuration = new(new ServerConfigurationEntity());
_service = ConnectionStringService.CreateDefault(); _service = ConnectionStringService.CreateDefault();
ParseConnectionStringCommand = ReactiveCommand.Create(ParseConnectionString); ParseConnectionStringCommand = ReactiveCommand.Create(ParseConnectionString);
@ -97,10 +98,10 @@ public class EditServerConfigurationViewModel : ViewModelBase
CloseCommand = ReactiveCommand.Create(() => { }); CloseCommand = ReactiveCommand.Create(() => { });
} }
public EditServerConfigurationViewModel(ServerConfiguration configuration) public EditServerConfigurationViewModel(ServerConfigurationEntity configuration)
: this() : this()
{ {
Configuration = configuration; Configuration = new ServerConfigurationViewModel(configuration);
} }
private void DetectFormat() private void DetectFormat()
@ -131,19 +132,21 @@ public class EditServerConfigurationViewModel : ViewModelBase
_ => new UrlCodec() _ => new UrlCodec()
}; };
var r = codec.TryParse(InputConnectionString); var r = codec.TryParse(InputConnectionString);
if (r.IsSuccess) descriptor = r.Value; if (r.IsSuccess)
descriptor = r.Value;
} }
else else
{ {
var r = _service.ParseToDescriptor(InputConnectionString); var r = _service.ParseToDescriptor(InputConnectionString);
if (r.IsSuccess) descriptor = r.Value; if (r.IsSuccess)
descriptor = r.Value;
} }
if (descriptor != null) if (descriptor != null)
{ {
// Map into our configuration (update existing) // Map into our configuration (update existing)
ServerConfigurationMapping.FromDescriptor(descriptor, Configuration); ServerConfigurationMapping.FromDescriptorInto(Configuration, descriptor);
// Also set sensible default OutputFormat to the detected/forced one // Also set a sensible default OutputFormat to the detected/forced one
if (forced == ForcedFormatOption.Auto) if (forced == ForcedFormatOption.Auto)
{ {
if (DetectedFormat.HasValue) OutputFormat = DetectedFormat.Value; if (DetectedFormat.HasValue) OutputFormat = DetectedFormat.Value;

View file

@ -0,0 +1,87 @@
using System;
using System.Reactive;
using Avalonia.Media;
using Npgsql;
using pgLabII.Model;
using ReactiveUI;
namespace pgLabII.ViewModels;
// UI ViewModel that wraps the persistence entity
public class ServerConfigurationViewModel : ReactiveObject
{
private readonly ServerConfigurationEntity _entity;
public ServerConfigurationViewModel(ServerConfigurationEntity entity)
{
_entity = entity ?? throw new ArgumentNullException(nameof(entity));
EditCommand = ReactiveCommand.Create(() =>
{
var vm = new EditServerConfigurationViewModel(_entity);
var window = new pgLabII.Views.EditServerConfigurationWindow(vm) { New = false };
window.Show();
});
ExploreCommand = ReactiveCommand.Create(() => { /* window coordination can be injected later */ });
}
public ServerConfigurationEntity Entity => _entity;
public Guid Id
{
get => _entity.Id;
set { if (_entity.Id != value) { _entity.Id = value; this.RaisePropertyChanged(); } }
}
public string Name
{
get => _entity.Name;
set { if (_entity.Name != value) { _entity.Name = value; this.RaisePropertyChanged(); } }
}
public string Host
{
get => _entity.Host;
set { if (_entity.Host != value) { _entity.Host = value; this.RaisePropertyChanged(); } }
}
public ushort Port
{
get => _entity.Port;
set { if (_entity.Port != value) { _entity.Port = value; this.RaisePropertyChanged(); } }
}
public string InitialDatabase
{
get => _entity.InitialDatabase;
set { if (_entity.InitialDatabase != value) { _entity.InitialDatabase = value; this.RaisePropertyChanged(); } }
}
public SslMode DefaultSslMode
{
get => _entity.SslMode;
set { if (_entity.SslMode != value) { _entity.SslMode = value; this.RaisePropertyChanged(); } }
}
public bool ColorEnabled
{
get => _entity.ColorEnabled;
set { if (_entity.ColorEnabled != value) { _entity.ColorEnabled = value; this.RaisePropertyChanged(); this.RaisePropertyChanged(nameof(BackgroundBrush)); } }
}
public Color Color
{
get => Color.FromUInt32((uint)_entity.ColorArgb);
set { var argb = unchecked((int)value.ToUInt32()); if (_entity.ColorArgb != argb) { _entity.ColorArgb = argb; this.RaisePropertyChanged(); this.RaisePropertyChanged(nameof(BackgroundBrush)); } }
}
public IBrush? BackgroundBrush => ColorEnabled ? new SolidColorBrush(Color) : null;
public ServerUser User
{
get => _entity.User;
set { if (!ReferenceEquals(_entity.User, value)) { _entity.User = value; this.RaisePropertyChanged(); } }
}
public ReactiveCommand<Unit, Unit> EditCommand { get; }
public ReactiveCommand<Unit, Unit> ExploreCommand { get; }
}

View file

@ -9,12 +9,11 @@ namespace pgLabII.ViewModels;
public class ServerListViewModel : ViewModelBase public class ServerListViewModel : ViewModelBase
{ {
public ObservableCollection<ServerConfiguration> ServerConfigurations { get; } = public ObservableCollection<ServerConfigurationViewModel> ServerConfigurations { get; } =
[ [
new ServerConfiguration() new (new()
{ {
Name = "Local pg15", Name = "Local pg15",
Color = Colors.Aquamarine,
ColorEnabled = true, ColorEnabled = true,
Host = "localhost", Host = "localhost",
Port = 5434, Port = 5434,
@ -24,22 +23,25 @@ public class ServerListViewModel : ViewModelBase
Name = "postgres", Name = "postgres",
Password = "admin", Password = "admin",
}, },
})
{
Color = Colors.Aquamarine,
}, },
new ServerConfiguration() new (new ()
{ {
Name = "Bar", Name = "Bar",
ColorEnabled = false, ColorEnabled = false,
Host = "db.host.nl" Host = "db.host.nl"
} }),
]; ];
public ReactiveCommand<ServerConfiguration, Unit> RemoveServerCommand { get; } public ReactiveCommand<ServerConfigurationViewModel, Unit> RemoveServerCommand { get; }
public ReactiveCommand<Unit, Unit> AddServerCommand { get; } public ReactiveCommand<Unit, Unit> AddServerCommand { get; }
public ServerListViewModel() public ServerListViewModel()
{ {
RemoveServerCommand = ReactiveCommand.Create<ServerConfiguration, Unit>((sc) => RemoveServerCommand = ReactiveCommand.Create<ServerConfigurationViewModel, Unit>((sc) =>
{ {
ServerConfigurations.Remove(sc); ServerConfigurations.Remove(sc);
return Unit.Default; return Unit.Default;