From 96db64d73eac20dbd0a6b7dccd93cff2ac169c26 Mon Sep 17 00:00:00 2001 From: Brett Samblanet Date: Mon, 21 Oct 2024 14:13:08 -0700 Subject: [PATCH] initial fix of duplicate registrations if AddFunctionsWorkerCore called twice (#2790) --- .../DefaultInputConverterInitializer.cs | 23 +++++++++ .../Hosting/ServiceCollectionExtensions.cs | 16 ++---- .../ServiceCollectionExtensionsTests.cs | 49 ++++++++++++++++++- 3 files changed, 74 insertions(+), 14 deletions(-) create mode 100644 src/DotNetWorker.Core/Hosting/DefaultInputConverterInitializer.cs diff --git a/src/DotNetWorker.Core/Hosting/DefaultInputConverterInitializer.cs b/src/DotNetWorker.Core/Hosting/DefaultInputConverterInitializer.cs new file mode 100644 index 000000000..67f207663 --- /dev/null +++ b/src/DotNetWorker.Core/Hosting/DefaultInputConverterInitializer.cs @@ -0,0 +1,23 @@ +// 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.Functions.Worker.Converters; +using Microsoft.Extensions.Options; + +namespace Microsoft.Azure.Functions.Worker.Core; + +internal class DefaultInputConverterInitializer : IConfigureOptions +{ + public void Configure(WorkerOptions options) + { + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + options.InputConverters.Register(); + } +} diff --git a/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs b/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs index 42502a2ea..65481e3ed 100644 --- a/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs +++ b/src/DotNetWorker.Core/Hosting/ServiceCollectionExtensions.cs @@ -81,7 +81,7 @@ public static IFunctionsWorkerApplicationBuilder AddFunctionsWorkerCore(this ISe } }); - services.AddSingleton(); + services.TryAddEnumerable(ServiceDescriptor.Singleton()); services.AddSingleton(NullLogWriter.Instance); services.AddSingleton(s => s.GetRequiredService()); services.AddSingleton(s => s.GetRequiredService()); @@ -123,18 +123,8 @@ public static IFunctionsWorkerApplicationBuilder AddFunctionsWorkerCore(this ISe /// internal static IServiceCollection AddDefaultInputConvertersToWorkerOptions(this IServiceCollection services) { - return services.Configure((workerOption) => - { - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - workerOption.InputConverters.Register(); - }); + services.TryAddEnumerable(ServiceDescriptor.Singleton, DefaultInputConverterInitializer>()); + return services; } /// diff --git a/test/DotNetWorkerTests/ServiceCollectionExtensionsTests.cs b/test/DotNetWorkerTests/ServiceCollectionExtensionsTests.cs index 4a40be0bf..a2bd75fb9 100644 --- a/test/DotNetWorkerTests/ServiceCollectionExtensionsTests.cs +++ b/test/DotNetWorkerTests/ServiceCollectionExtensionsTests.cs @@ -1,7 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the MIT License. See License.txt in the project root for license information. -using Microsoft.Extensions.DependencyInjection; +using System.Linq; +using Microsoft.Azure.Functions.Worker.Logging; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; using Xunit; @@ -26,5 +29,49 @@ public void ConfigureOptions_IsCalled() Assert.True(configured); } + + [Fact] + public void DefaultInputConverters_RegisteredOnce() + { + var serviceColl = new ServiceCollection(); + serviceColl.AddFunctionsWorkerDefaults(); + serviceColl.AddFunctionsWorkerDefaults(); + + var services = serviceColl.BuildServiceProvider(); + + // request the worker options, which forces their configuration to be called. + var workerOptions = services.GetService>().Value; + + // Ensure that even though we've called the registration twice, only one + // set of default input converters is registered. + var count = workerOptions.InputConverters.Count(); + Assert.Equal(9, count); + } + + [Fact] + public void LoggerProvider_RegisteredOnce() + { + var serviceColl = new ServiceCollection(); + serviceColl.AddFunctionsWorkerDefaults(); + serviceColl.AddFunctionsWorkerDefaults(); + + var services = serviceColl.BuildServiceProvider(); + + var loggerProviders = services.GetServices(); + + // Ensure that even though we've called the registration twice, only one + // WorkerLoggerProvider is registered. + Assert.Single(loggerProviders.Where(p => p is WorkerLoggerProvider)); + } + + [Fact] + public void SameBuilder_Returned() + { + var serviceColl = new ServiceCollection(); + var builder1 = serviceColl.AddFunctionsWorkerCore(); + var builder2 = serviceColl.AddFunctionsWorkerCore(); + + Assert.Same(builder1, builder2); + } } }