0
0
Files
2025-12-07 16:37:00 +11:00

126 lines
5.4 KiB
C#

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<T> OptionsTransformer<T>(OptionsBuilder<T> transform) where T : class;
/// <summary>
/// Adds <see cref="PostgreSQLNotificationConfigurationReloader"/> (a <see cref="Microsoft.Extensions.Hosting.IHostedService"/> implementation)
/// and supporting services to the <see cref="IServiceCollection"/>.
/// </summary>
/// <typeparam name="TDbContext">Type of the <see cref="DbContext"/> to use to obtain the underlying database connection</typeparam>
/// <param name="services">The service collection to add the services too</param>
/// <returns>The service collection it was called on now with added services</returns>
public static IServiceCollection AddPostgreSQLNotificationConfigurationReloadService<TDbContext>(this IServiceCollection services)
where TDbContext : DbContext
{
return services.AddPostgreSQLNotificationConfigurationReloadService<TDbContext>(static _ => { });
}
/// <summary>
/// Adds <see cref="PostgreSQLNotificationConfigurationReloader"/> (a <see cref="Microsoft.Extensions.Hosting.IHostedService"/> implementation)
/// and supporting services to the <see cref="IServiceCollection"/>.
/// </summary>
/// <typeparam name="TDbContext">Type of the <see cref="DbContext"/> to use to obtain the underlying database connection</typeparam>
/// <param name="services">The service collection to add the services too</param>
/// <param name="configure">
/// Action to manually configure the <see cref="Options"/> instance consumed by <see cref="PostgreSQLNotificationConfigurationReloader"/> eg.
/// <code>
/// options => {
/// options.DebounceInterval = TimeSpan.FromSeconds(2);
/// options.ChannelName = "awesome_channel";
/// }
/// </code>
/// </param>
/// <returns>The service collection it was called on now with added services</returns>
public static IServiceCollection AddPostgreSQLNotificationConfigurationReloadService<TDbContext>(this IServiceCollection services, Action<Options> configure)
where TDbContext : DbContext
{
var optionsBuilder = services.AddCoreServices<TDbContext>();
optionsBuilder.Configure(configure);
return services;
}
/// <summary>
/// Adds <see cref="PostgreSQLNotificationConfigurationReloader"/> (a <see cref="Microsoft.Extensions.Hosting.IHostedService"/> implementation)
/// and supporting services to the <see cref="IServiceCollection"/>.
/// </summary>
/// <typeparam name="TDbContext">Type of the <see cref="DbContext"/> to use to obtain the underlying database connection</typeparam>
/// <param name="services">The service collection to add the services too</param>
/// <param name="optionsTransform">
/// Transformer delegate to apply to the <see cref="Options"/> instance consumed by <see cref="PostgreSQLNotificationConfigurationReloader"/> eg.
/// <code>
/// optionsBuilder => optionsBuilder.Bind(context.Configuration)
/// </code>
/// </param>
/// <returns>The service collection it was called on now with added services</returns>
public static IServiceCollection AddPostgreSQLNotificationConfigurationReloadService<TDbContext>(this IServiceCollection services, OptionsTransformer<Options> optionsTransform)
where TDbContext : DbContext
{
var optionsBuilder = services.AddCoreServices<TDbContext>();
optionsTransform(optionsBuilder);
return services;
}
private static OptionsBuilder<Options> AddCoreServices<TDbContext>(this IServiceCollection services)
where TDbContext : DbContext
{
services.AddSingleton(static provider => provider.GetRequiredService<TDbContext>().Database)
.AddSingleton(static provider =>
{
var configRoot = (IConfigurationRoot)provider.GetRequiredService<IConfiguration>(); // DEBT: Is this cast always safe?
return configRoot.Providers.OfType<IEntityFrameworkCoreDbSetConfigurationProvider>().Single();
})
.AddHostedService<PostgreSQLNotificationConfigurationReloader>();
return services.AddOptions<Options>().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();
*
*/