From 775d7cb2ef6769da60a5cacb22b089185e9597d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jean-Thierry=20K=C3=A9chichian?= Date: Sat, 2 Dec 2023 07:24:28 +0100 Subject: [PATCH] IAsyncConfigureOptions (#14759) --- .../BlobOptions.cs | 4 +- .../BlobOptionsSetup.cs | 8 +-- .../Startup.cs | 10 +--- .../Extensions/IAsyncConfigureOptions.cs | 14 +++++ .../Builders/Extensions/IAsyncOptions.cs | 9 +++ .../Extensions/ServiceCollectionExtensions.cs | 55 ++++++++++++++++++- 6 files changed, 83 insertions(+), 17 deletions(-) create mode 100644 src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncConfigureOptions.cs create mode 100644 src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncOptions.cs diff --git a/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptions.cs b/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptions.cs index 1db47d2a1e2..498696e08c5 100644 --- a/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptions.cs +++ b/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptions.cs @@ -1,6 +1,8 @@ +using Microsoft.Extensions.Options; + namespace OrchardCore.DataProtection.Azure; -public class BlobOptions +public class BlobOptions : IAsyncOptions { public string ConnectionString { get; set; } diff --git a/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptionsSetup.cs b/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptionsSetup.cs index a1652835ad0..99017fbc6cc 100644 --- a/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptionsSetup.cs +++ b/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/BlobOptionsSetup.cs @@ -11,7 +11,7 @@ namespace OrchardCore.DataProtection.Azure; -public class BlobOptionsSetup +public class BlobOptionsSetup : IAsyncConfigureOptions { private readonly FluidParser _fluidParser = new(); @@ -32,14 +32,14 @@ public BlobOptionsSetup( _logger = logger; } - public async Task ConfigureAsync(BlobOptions options) + public async ValueTask ConfigureAsync(BlobOptions options) { _configuration.Bind("OrchardCore_DataProtection_Azure", options); await ConfigureContainerNameAsync(options); await ConfigureBlobNameAsync(options); } - private async Task ConfigureContainerNameAsync(BlobOptions options) + private async ValueTask ConfigureContainerNameAsync(BlobOptions options) { try { @@ -78,7 +78,7 @@ private async Task ConfigureContainerNameAsync(BlobOptions options) } } - private async Task ConfigureBlobNameAsync(BlobOptions options) + private async ValueTask ConfigureBlobNameAsync(BlobOptions options) { if (string.IsNullOrEmpty(options.BlobName)) { diff --git a/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/Startup.cs b/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/Startup.cs index 55eae14677c..084236042b2 100644 --- a/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/Startup.cs +++ b/src/OrchardCore.Modules/OrchardCore.DataProtection.Azure/Startup.cs @@ -29,8 +29,7 @@ public override void ConfigureServices(IServiceCollection services) if (!string.IsNullOrWhiteSpace(connectionString)) { services - .AddSingleton(new BlobOptions()) - .AddTransient() + .Configure() .AddDataProtection() .PersistKeysToAzureBlobStorage(sp => { @@ -40,13 +39,6 @@ public override void ConfigureServices(IServiceCollection services) options.ContainerName, options.BlobName); }); - - services.Initialize(async sp => - { - var options = sp.GetRequiredService(); - var setup = sp.GetRequiredService(); - await setup.ConfigureAsync(options); - }); } else { diff --git a/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncConfigureOptions.cs b/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncConfigureOptions.cs new file mode 100644 index 00000000000..3567382bb4e --- /dev/null +++ b/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncConfigureOptions.cs @@ -0,0 +1,14 @@ +using System.Threading.Tasks; + +namespace Microsoft.Extensions.Options; + +/// +/// Used to configure asynchronously a type of options just after a tenant container is created. +/// +public interface IAsyncConfigureOptions where TOptions : class, IAsyncOptions +{ + /// + /// Configures asynchronously an options instance. + /// + ValueTask ConfigureAsync(TOptions options); +} diff --git a/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncOptions.cs b/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncOptions.cs new file mode 100644 index 00000000000..5c6fb037f5e --- /dev/null +++ b/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/IAsyncOptions.cs @@ -0,0 +1,9 @@ +namespace Microsoft.Extensions.Options; + +/// +/// Marks a type of options intended to be registered as a singleton and configured asynchronously +/// by an just after a tenant container is created. +/// +public interface IAsyncOptions +{ +} diff --git a/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/ServiceCollectionExtensions.cs b/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/ServiceCollectionExtensions.cs index 7a88bbde65e..88350dd775e 100644 --- a/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/ServiceCollectionExtensions.cs +++ b/src/OrchardCore/OrchardCore.Abstractions/Shell/Builders/Extensions/ServiceCollectionExtensions.cs @@ -1,5 +1,7 @@ using System; +using System.Linq; using System.Threading.Tasks; +using Microsoft.Extensions.Options; using OrchardCore.Environment.Shell.Builders; namespace Microsoft.Extensions.DependencyInjection; @@ -7,8 +9,55 @@ namespace Microsoft.Extensions.DependencyInjection; public static class ServiceCollectionExtensions { /// - /// Registers a delegate to be invoked asynchronously after a tenant container is created. + /// Registers a delegate to be invoked asynchronously just after a tenant container is created. /// - public static IServiceCollection Initialize(this IServiceCollection services, Func _initializeAsync) - => services.Configure(options => options.Initializers.Add(_initializeAsync)); + public static IServiceCollection Initialize(this IServiceCollection services, Func initializeAsync) + => services.Configure(options => options.Initializers.Add(initializeAsync)); + + /// + /// Registers a delegate used to configure asynchronously a type of options just after a tenant container is created. + /// + public static IServiceCollection Configure( + this IServiceCollection services, Func configureAsync) + where TOptions : class, IAsyncOptions, new() + { + if (!services.Any(d => d.ServiceType == typeof(TOptions))) + { + services.AddSingleton(new TOptions()); + } + + services.Initialize(async sp => + { + var options = sp.GetRequiredService(); + await configureAsync(sp, options); + }); + + return services; + } + + /// + /// Registers an used to configure + /// asynchronously a type of options just after a tenant container is created. + /// + public static IServiceCollection Configure(this IServiceCollection services) + where TOptions : class, IAsyncOptions, new() where TConfigure : IAsyncConfigureOptions + { + if (!services.Any(d => d.ServiceType == typeof(TOptions))) + { + services.AddSingleton(new TOptions()); + } + + if (!services.Any(d => d.ServiceType == typeof(TConfigure))) + { + services.AddTransient(typeof(TConfigure)); + services.Initialize(async sp => + { + var options = sp.GetRequiredService(); + var setup = sp.GetRequiredService(); + await setup.ConfigureAsync(options); + }); + } + + return services; + } }