From 804c1c99a0bfb4cb5d07dc514938fcad0fd9e87c Mon Sep 17 00:00:00 2001 From: Chase Miller Date: Mon, 14 Apr 2025 09:34:53 -0400 Subject: [PATCH] Make AWSOptions a DTO and Add [Try]Add[Keyed]AWSService methods that take an optionsFunc arg --- .../AWSOptions.cs | 17 ---- .../AWSOptionsExtensions.cs | 27 +++++++ .../ServiceCollectionExtensions.cs | 80 +++++++++++++++++-- .../NETCore.SetupTests/ConfigurationTests.cs | 1 + 4 files changed, 103 insertions(+), 22 deletions(-) create mode 100644 extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs index dbf4d2feb1dd..21e9e2611f75 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptions.cs @@ -55,11 +55,6 @@ public class AWSOptions /// public string ExternalId { get; set; } - /// - /// - /// - public IAWSCredentialsFactory CredentialsFactory { get; set; } - /// /// AWS Credentials used for creating service clients. If this is set it overrides the Profile property. /// @@ -97,18 +92,6 @@ internal set /// public LoggingSetting Logging { get; set; } - /// - /// Create a service client for the specified service interface using the options set in this instance. - /// For example if T is set to IAmazonS3 then the AmazonS3ServiceClient which implements IAmazonS3 is created - /// and returned. - /// - /// The service interface that a service client will be created for. - /// The service client that implements the service interface. - public T CreateServiceClient() where T : class, IAmazonService - { - return new ClientFactory(this, CredentialsFactory, (ILogger)null).CreateServiceClient() as T; - } - /// /// Container for logging settings of the SDK /// diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs new file mode 100644 index 000000000000..29225771e992 --- /dev/null +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/AWSOptionsExtensions.cs @@ -0,0 +1,27 @@ +using Amazon.Extensions.NETCore.Setup; +using Amazon.Runtime; + +namespace AWSSDK.Extensions.NETCore.Setup +{ + /// + /// + /// + public static class AWSOptionsExtensions + { + /// + /// Create a service client for the specified service interface using the options set in this instance. + /// For example if T is set to IAmazonS3 then the AmazonS3ServiceClient which implements IAmazonS3 is created + /// and returned. + /// + /// The service interface that a service client will be created for. + /// The service client that implements the service interface. + public static T CreateServiceClient(this AWSOptions options, IAWSCredentialsFactory credentialsFactory = null) + where T : class, IAmazonService + { + credentialsFactory = credentialsFactory ?? new DefaultAWSCredentialsFactory(options); + var clientFactory = new ClientFactory(options, credentialsFactory, null); + + return clientFactory.CreateServiceClient() as T; + } + } +} \ No newline at end of file diff --git a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs index 1808316a2359..4044dfe4bba5 100644 --- a/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs +++ b/extensions/src/AWSSDK.Extensions.NETCore.Setup/ServiceCollectionExtensions.cs @@ -196,7 +196,7 @@ public static IServiceCollection TryAddDefaultAWSOptions( /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddAWSService(this IServiceCollection collection, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return AddAWSService(collection, null, lifetime); + return AddAWSService(collection, options: null, lifetime); } /// @@ -216,6 +216,23 @@ public static IServiceCollection AddAWSService(this IServiceCollection collec return collection; } + /// + /// Adds the Amazon service client to the dependency injection framework. The Amazon service client is not + /// created until it is requested. If the ServiceLifetime property is set to Singleton, the default, then the same + /// instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3. + /// + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection AddAWSService(this IServiceCollection collection, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.Add(descriptor); + return collection; + } + /// /// Adds the Amazon service client to the dependency injection framework if the service type hasn't already been registered. /// The Amazon service client is not created until it is requested. If the ServiceLifetime property is set to Singleton, @@ -227,7 +244,7 @@ public static IServiceCollection AddAWSService(this IServiceCollection collec /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddAWSService(this IServiceCollection collection, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return TryAddAWSService(collection, null, lifetime); + return TryAddAWSService(collection, options: null, lifetime); } /// @@ -247,6 +264,23 @@ public static IServiceCollection TryAddAWSService(this IServiceCollection col return collection; } + /// + /// Adds the Amazon service client to the dependency injection framework if the service type hasn't already been registered. + /// The Amazon service client is not created until it is requested. If the ServiceLifetime property is set to Singleton, + /// the default, then the same instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3. + /// + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection TryAddAWSService(this IServiceCollection collection, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), sp => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.TryAdd(descriptor); + return collection; + } + #if NET8_0_OR_GREATER /// @@ -261,7 +295,7 @@ public static IServiceCollection TryAddAWSService(this IServiceCollection col /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return AddKeyedAWSService(collection, serviceKey, null, lifetime); + return AddKeyedAWSService(collection, serviceKey, options: null, lifetime); } /// @@ -281,6 +315,24 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c collection.Add(descriptor); return collection; } + + /// + /// Adds the Amazon service client to the dependency injection framework with a key. The Amazon service client is not + /// created until it is requested. If the ServiceLifetime property is set to Singleton, the default, then the same + /// instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3 + /// + /// The key with which the service will be added in the dependency injection framework. + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection AddKeyedAWSService(this IServiceCollection collection, object serviceKey, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.Add(descriptor); + return collection; + } /// /// Adds the Amazon service client to the dependency injection framework with a key if the service type hasn't already been registered with the same key. @@ -294,7 +346,7 @@ public static IServiceCollection AddKeyedAWSService(this IServiceCollection c /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService { - return TryAddKeyedAWSService(collection, serviceKey, null, lifetime); + return TryAddKeyedAWSService(collection, serviceKey, options: null, lifetime); } /// @@ -314,13 +366,31 @@ public static IServiceCollection TryAddKeyedAWSService(this IServiceCollectio collection.TryAdd(descriptor); return collection; } + + /// + /// Adds the Amazon service client to the dependency injection framework with a key if the service type hasn't already been registered with the same key. + /// The Amazon service client is not created until it is requested. If the ServiceLifetime property is set to Singleton, the default, then the same + /// instance will be reused for the lifetime of the process and the object should not be disposed. + /// + /// The AWS service interface, like IAmazonS3 + /// + /// The key with which the service will be added in the dependency injection framework. + /// A func that resolves the AWS options used to create the service client overriding the default AWS options added using AddDefaultAWSOptions. + /// The lifetime of the service client created. The default is Singleton. + /// Returns back the IServiceCollection to continue the fluent system of IServiceCollection. + public static IServiceCollection TryAddKeyedAWSService(this IServiceCollection collection, object serviceKey, Func optionsFunc, ServiceLifetime lifetime = ServiceLifetime.Singleton) where T : IAmazonService + { + var descriptor = new ServiceDescriptor(typeof(T), serviceKey, (sp, _) => CreateServiceClient(optionsFunc(sp), sp), lifetime); + collection.TryAdd(descriptor); + return collection; + } #endif private static object CreateServiceClient(AWSOptions options, IServiceProvider sp) where T : IAmazonService { var logger = sp.GetService(); var awsOptions = options ?? sp.GetService() ?? new AWSOptions(); - var credentialsFactory = awsOptions.CredentialsFactory ?? sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); + var credentialsFactory = sp.GetService() ?? new DefaultAWSCredentialsFactory(awsOptions, logger); var factory = new ClientFactory(awsOptions, credentialsFactory, logger); diff --git a/extensions/test/NETCore.SetupTests/ConfigurationTests.cs b/extensions/test/NETCore.SetupTests/ConfigurationTests.cs index 731e5f2edefb..cf7aeaa97d25 100644 --- a/extensions/test/NETCore.SetupTests/ConfigurationTests.cs +++ b/extensions/test/NETCore.SetupTests/ConfigurationTests.cs @@ -10,6 +10,7 @@ using Amazon; using Amazon.S3; using Amazon.Runtime; +using AWSSDK.Extensions.NETCore.Setup; namespace NETCore.SetupTests {