using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Options; using Options = RAIC.Extensions.Configuration.EntityFrameworkCore.PostgreSQL.PostgreSQLNotificationConfigurationReloaderOptions; namespace RAIC.Extensions.Configuration.EntityFrameworkCore.PostgreSQL.Extensions; public static class ServiceCollectionExtensions { public delegate OptionsBuilder OptionsTransformer(OptionsBuilder transform) where T : class; /// /// Adds (a implementation) /// and supporting services to the . /// /// Type of the to use to obtain the underlying database connection /// The service collection to add the services too /// The service collection it was called on now with added services public static IServiceCollection AddPostgreSQLNotificationConfigurationReloadService(this IServiceCollection services) where TDbContext : DbContext { return services.AddPostgreSQLNotificationConfigurationReloadService(static _ => { }); } /// /// Adds (a implementation) /// and supporting services to the . /// /// Type of the to use to obtain the underlying database connection /// The service collection to add the services too /// /// Action to manually configure the instance consumed by eg. /// /// options => { /// options.DebounceInterval = TimeSpan.FromSeconds(2); /// options.ChannelName = "awesome_channel"; /// } /// /// /// The service collection it was called on now with added services public static IServiceCollection AddPostgreSQLNotificationConfigurationReloadService(this IServiceCollection services, Action configure) where TDbContext : DbContext { var optionsBuilder = services.AddCoreServices(); optionsBuilder.Configure(configure); return services; } /// /// Adds (a implementation) /// and supporting services to the . /// /// Type of the to use to obtain the underlying database connection /// The service collection to add the services too /// /// Transformer delegate to apply to the instance consumed by eg. /// /// optionsBuilder => optionsBuilder.Bind(context.Configuration) /// /// /// The service collection it was called on now with added services public static IServiceCollection AddPostgreSQLNotificationConfigurationReloadService(this IServiceCollection services, OptionsTransformer optionsTransform) where TDbContext : DbContext { var optionsBuilder = services.AddCoreServices(); optionsTransform(optionsBuilder); return services; } private static OptionsBuilder AddCoreServices(this IServiceCollection services) where TDbContext : DbContext { services.AddSingleton(static provider => provider.GetRequiredService().Database) .AddSingleton(static provider => { var configRoot = (IConfigurationRoot)provider.GetRequiredService(); // DEBT: Is this cast always safe? return configRoot.Providers.OfType().Single(); }) .AddHostedService(); return services.AddOptions().ValidateDataAnnotations().ValidateOnStart(); } } /* * CREATE OR REPLACE FUNCTION settings_poc.notify_setting_change() RETURNS trigger AS $$ BEGIN PERFORM pg_notify('setting_channel', concat(NEW.key,'=',NEW.value)); RETURN NEW; END; $$ LANGUAGE plpgsql; CREATE OR REPLACE FUNCTION settings_poc.notify_setting_remove() RETURNS trigger AS $$ BEGIN PERFORM pg_notify('setting_channel', OLD.key); RETURN OLD; END; $$ LANGUAGE plpgsql; CREATE TRIGGER setting_insert_trigger AFTER INSERT ON settings_poc.settings FOR EACH ROW EXECUTE FUNCTION settings_poc.notify_setting_change(); CREATE TRIGGER setting_update_trigger AFTER UPDATE ON settings_poc.settings FOR EACH ROW WHEN (NEW.value <> OLD.value) EXECUTE FUNCTION settings_poc.notify_setting_change(); CREATE TRIGGER setting_delete_trigger AFTER DELETE ON settings_poc.settings FOR EACH ROW EXECUTE FUNCTION settings_poc.notify_setting_remove(); * */