using Microsoft.Extensions.Configuration; using Npgsql; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks; namespace Core.Configurations.PostgresqlConfigurationBuilder { public class PostgresConfigurationSource : IConfigurationSource { private readonly string _connectionString; private readonly string _channel; private readonly string _configurationQuery; public PostgresConfigurationSource(string connectionString, string channel, string configurationQuery) { _connectionString = connectionString ?? throw new ArgumentNullException(nameof(connectionString)); _channel = channel ?? throw new ArgumentNullException(nameof(channel)); _configurationQuery = configurationQuery ?? throw new ArgumentNullException(nameof(configurationQuery)); } public IConfigurationProvider Build(IConfigurationBuilder builder) { return new PostgresConfigurationProvider(_connectionString, _channel, _configurationQuery); } } public class PostgresConfigurationProvider : ConfigurationProvider, IDisposable { private readonly string _connectionString; private readonly string _channel; private readonly string _configurationQuery; private readonly NpgsqlConnection _listenerConnection; private bool _disposedValue; public PostgresConfigurationProvider(string connectionString, string channel, string configurationQuery) { _connectionString = connectionString; _channel = channel; _configurationQuery = configurationQuery; _listenerConnection = new NpgsqlConnection(connectionString); // Start listening for notifications StartListening(); } private async void StartListening() { try { await _listenerConnection.OpenAsync(); _listenerConnection.Notification += OnNotificationReceived; using var cmd = new NpgsqlCommand($"LISTEN {_channel};", _listenerConnection); await cmd.ExecuteNonQueryAsync(); } catch (Exception ex) { // Log error and possibly retry Console.WriteLine($"Error starting listener: {ex.Message}"); } } private void OnNotificationReceived(object sender, NpgsqlNotificationEventArgs e) { if (e.Channel == _channel) { // Reload configuration and notify Load(); OnReload(); } } public override void Load() { var data = new Dictionary(StringComparer.OrdinalIgnoreCase); using (var connection = new NpgsqlConnection(_connectionString)) { connection.Open(); using var cmd = new NpgsqlCommand(_configurationQuery, connection); using var reader = cmd.ExecuteReader(); while (reader.Read()) { string key = reader.GetString(0); string value = reader.GetString(1); data[key] = value; } } Data = data; } protected virtual void Dispose(bool disposing) { if (!_disposedValue) { if (disposing) { _listenerConnection?.Dispose(); } _disposedValue = true; } } public void Dispose() { Dispose(disposing: true); GC.SuppressFinalize(this); } } }