diff --git a/extensions/Worker.Extensions.CosmosDB/release_notes.md b/extensions/Worker.Extensions.CosmosDB/release_notes.md index 4514dbf4b..a0bdf4810 100644 --- a/extensions/Worker.Extensions.CosmosDB/release_notes.md +++ b/extensions/Worker.Extensions.CosmosDB/release_notes.md @@ -4,8 +4,19 @@ - My change description (#PR/#issue) --> -### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB 4.8.1 +### Microsoft.Azure.Functions.Worker.Extensions.CosmosDB 4.9.0 -- Updating `Microsoft.Azure.WebJobs.Extensions.CosmosDB` reference to 4.6.1 -- Updating `Microsoft.Extensions.Azure` reference to 1.7.3 -- Updating `Microsoft.Azure.Cosmos` reference to 3.39.1 +- Implement `CosmosDBExtensionOptions` to allow configuration of the CosmosDB service client via `CosmosClientOptions` (#2483) + +#### Example Usage + +```csharp +.ConfigureFunctionsWorkerDefaults((builder) => +{ + builder.ConfigureCosmosDBExtensionOptions((options) => + { + options.ClientOptions.ConnectionMode = ConnectionMode.Direct; + options.ClientOptions.ApplicationName = "MyApp"; + }); +}) +``` diff --git a/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptions.cs b/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptions.cs index 8a2eb9e1b..403ee836a 100644 --- a/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptions.cs +++ b/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptions.cs @@ -9,6 +9,12 @@ namespace Microsoft.Azure.Functions.Worker { + /// + /// Internal options for configuring the CosmosDB binding. + /// This class is used internally by the Azure Functions runtime to manage the CosmosDB connection and clients. + /// It is not intended to be used directly in user code. + /// Any public configuration options should be set on the class, which is publicly accessible. + /// internal class CosmosDBBindingOptions { public string? ConnectionName { get; set; } @@ -19,7 +25,7 @@ internal class CosmosDBBindingOptions public TokenCredential? Credential { get; set; } - public CosmosSerializer? Serializer { get; set; } + public CosmosDBExtensionOptions? CosmosExtensionOptions { get; set; } internal string BuildCacheKey(string connection, string region) => $"{connection}|{region}"; @@ -32,24 +38,23 @@ internal virtual CosmosClient GetClient(string preferredLocations = "") throw new ArgumentNullException(nameof(ConnectionName)); } - string cacheKey = BuildCacheKey(ConnectionName!, preferredLocations); - - CosmosClientOptions cosmosClientOptions = new () - { - ConnectionMode = ConnectionMode.Gateway - }; - - if (!string.IsNullOrEmpty(preferredLocations)) + if (CosmosExtensionOptions is null) { - cosmosClientOptions.ApplicationPreferredRegions = Utilities.ParsePreferredLocations(preferredLocations); + CosmosExtensionOptions = new CosmosDBExtensionOptions(); } - if (Serializer is not null) + string cacheKey = BuildCacheKey(ConnectionName!, preferredLocations); + + // Do not override if preferred locations is configured via CosmosClientOptions + if (!string.IsNullOrEmpty(preferredLocations) && CosmosExtensionOptions.ClientOptions.ApplicationPreferredRegions is null) { - cosmosClientOptions.Serializer = Serializer; + // Clone to avoid modifying the original options + CosmosClientOptions cosmosClientOptions = CosmosExtensionOptions.ClientOptions.MemberwiseClone(); + cosmosClientOptions.ApplicationPreferredRegions = Utilities.ParsePreferredLocations(preferredLocations); + return ClientCache.GetOrAdd(cacheKey, (c) => CreateService(cosmosClientOptions)); } - return ClientCache.GetOrAdd(cacheKey, (c) => CreateService(cosmosClientOptions)); + return ClientCache.GetOrAdd(cacheKey, (c) => CreateService(CosmosExtensionOptions.ClientOptions)); } private CosmosClient CreateService(CosmosClientOptions cosmosClientOptions) @@ -59,4 +64,4 @@ private CosmosClient CreateService(CosmosClientOptions cosmosClientOptions) : new CosmosClient(ConnectionString, cosmosClientOptions); // Connection string based auth } } -} \ No newline at end of file +} diff --git a/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptionsSetup.cs b/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptionsSetup.cs index 294efb3ad..4a154bddc 100644 --- a/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptionsSetup.cs +++ b/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBBindingOptionsSetup.cs @@ -2,6 +2,7 @@ // Licensed under the MIT License. See License.txt in the project root for license information. using System; +using Microsoft.Azure.Cosmos; using Microsoft.Azure.Functions.Worker.Extensions; using Microsoft.Azure.Functions.Worker.Extensions.CosmosDB; using Microsoft.Extensions.Azure; @@ -15,12 +16,14 @@ internal class CosmosDBBindingOptionsSetup : IConfigureNamedOptions _workerOptions; + private readonly IOptionsMonitor _cosmosExtensionOptions; - public CosmosDBBindingOptionsSetup(IConfiguration configuration, AzureComponentFactory componentFactory, IOptionsMonitor workerOptions) + public CosmosDBBindingOptionsSetup(IConfiguration configuration, AzureComponentFactory componentFactory, IOptionsMonitor workerOptions, IOptionsMonitor cosmosExtensionOptions) { _configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); _componentFactory = componentFactory ?? throw new ArgumentNullException(nameof(componentFactory)); _workerOptions = workerOptions ?? throw new ArgumentNullException(nameof(workerOptions)); + _cosmosExtensionOptions = cosmosExtensionOptions ?? throw new ArgumentNullException(nameof(cosmosExtensionOptions)); } public void Configure(CosmosDBBindingOptions options) @@ -56,7 +59,7 @@ public void Configure(string connectionName, CosmosDBBindingOptions options) options.Credential = _componentFactory.CreateTokenCredential(connectionSection); } - options.Serializer = new WorkerCosmosSerializer(_workerOptions.CurrentValue.Serializer); + options.CosmosExtensionOptions = _cosmosExtensionOptions.CurrentValue; } } -} \ No newline at end of file +} diff --git a/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBExtensionOptions.cs b/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBExtensionOptions.cs new file mode 100644 index 000000000..e9d7b3c75 --- /dev/null +++ b/extensions/Worker.Extensions.CosmosDB/src/Config/CosmosDBExtensionOptions.cs @@ -0,0 +1,15 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using Microsoft.Azure.Cosmos; + +namespace Microsoft.Azure.Functions.Worker +{ + public class CosmosDBExtensionOptions + { + /// + /// Gets or sets the CosmosClientOptions. + /// + public CosmosClientOptions ClientOptions { get; set; } = new() { ConnectionMode = ConnectionMode.Gateway }; + } +} diff --git a/extensions/Worker.Extensions.CosmosDB/src/Extensions/CloningExtensions.cs b/extensions/Worker.Extensions.CosmosDB/src/Extensions/CloningExtensions.cs new file mode 100644 index 000000000..f7146c1d1 --- /dev/null +++ b/extensions/Worker.Extensions.CosmosDB/src/Extensions/CloningExtensions.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the MIT License. See License.txt in the project root for license information. + +using System; +using System.Reflection; +using Microsoft.Azure.Cosmos; + +namespace Microsoft.Azure.Functions.Worker +{ + internal static class CloningExtensions + { + public static T MemberwiseClone(this T original) + { + if (original is null) + { + throw new ArgumentNullException(nameof(original)); + } + + // Get the type of the object + Type type = original.GetType(); + + // Get the internal Clone method + MethodInfo cloneMethod = type.GetMethod("MemberwiseClone", BindingFlags.Instance | BindingFlags.NonPublic); + + if (cloneMethod is null) + { + throw new InvalidOperationException("The MemberwiseClone method could not be found."); + } + + // Invoke the Clone method + return (T)cloneMethod.Invoke(original, null); + } + } +} diff --git a/extensions/Worker.Extensions.CosmosDB/src/Extensions/FunctionsWorkerApplicationBuilderExtensions.cs b/extensions/Worker.Extensions.CosmosDB/src/Extensions/FunctionsWorkerApplicationBuilderExtensions.cs index fc904ef25..dc8743bbd 100644 --- a/extensions/Worker.Extensions.CosmosDB/src/Extensions/FunctionsWorkerApplicationBuilderExtensions.cs +++ b/extensions/Worker.Extensions.CosmosDB/src/Extensions/FunctionsWorkerApplicationBuilderExtensions.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. +using System; +using Microsoft.Azure.Cosmos; using Microsoft.Extensions.Azure; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection.Extensions; @@ -27,9 +29,30 @@ public static IFunctionsWorkerApplicationBuilder ConfigureCosmosDBExtension(this builder.Services.AddAzureClientsCore(); // Adds AzureComponentFactory builder.Services.AddOptions(); + builder.Services.AddOptions() + .Configure>((cosmos, worker) => + { + if (cosmos.ClientOptions.Serializer is null) + { + cosmos.ClientOptions.Serializer = new WorkerCosmosSerializer(worker.Value.Serializer); + } + }); + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, CosmosDBBindingOptionsSetup>()); return builder; } + + /// + /// Configures the CosmosDBExtensionOptions for the Functions Worker Cosmos extension. + /// + /// The IFunctionsWorkerApplicationBuilder to add the configuration to. + /// An Action to configure the CosmosDBExtensionOptions. + /// The same instance of the for chaining. + public static IFunctionsWorkerApplicationBuilder ConfigureCosmosDBExtensionOptions(this IFunctionsWorkerApplicationBuilder builder, Action options) + { + builder.Services.Configure(options); + return builder; + } } } diff --git a/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj b/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj index 0835d2793..d374523d9 100644 --- a/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj +++ b/extensions/Worker.Extensions.CosmosDB/src/Worker.Extensions.CosmosDB.csproj @@ -6,7 +6,7 @@ Azure Cosmos DB extensions for .NET isolated functions - 4.8.1 + 4.9.0 false