From c955b95cbbf4645f17cfb54c25deb65056194417 Mon Sep 17 00:00:00 2001 From: Fabio Cavalcante Date: Tue, 20 Aug 2024 20:20:05 -0700 Subject: [PATCH] Shimming legacy .NET worker JsonFunctionProvider to ensure backwards compatibility --- release_notes.md | 1 + .../JobHostScopedServiceProviderFactory.cs | 63 ++++++++++++++++++- 2 files changed, 61 insertions(+), 3 deletions(-) diff --git a/release_notes.md b/release_notes.md index 8ea6028489..50d091a7d4 100644 --- a/release_notes.md +++ b/release_notes.md @@ -17,3 +17,4 @@ - Update grpc-protobuf to 1.64.0 and application insights agent version to 3.5.2 - Resolved thread safety issue in the `GrpcWorkerChannel.LoadResponse` method. (#10352) - Worker termination path updated with sanitized logging (#10367) +- Added logic to shim older versions of the .NET Worker JsonFunctiopnProvider to ensure backwards compatibility (#10410) diff --git a/src/WebJobs.Script.WebHost/DependencyInjection/JobHostScopedServiceProviderFactory.cs b/src/WebJobs.Script.WebHost/DependencyInjection/JobHostScopedServiceProviderFactory.cs index df1b35b6c9..ef747f6071 100644 --- a/src/WebJobs.Script.WebHost/DependencyInjection/JobHostScopedServiceProviderFactory.cs +++ b/src/WebJobs.Script.WebHost/DependencyInjection/JobHostScopedServiceProviderFactory.cs @@ -62,7 +62,7 @@ public IServiceProvider CreateServiceProvider(IServiceCollection services) throw new HostInitializationException("Invalid host services detected.", ex); } - ShimBreakingChange(services); + ShimBreakingChanges(services, _logger); // Start from the root (web app level) as a base var jobHostServices = _rootProvider.CreateChildContainer(_rootServices); @@ -76,12 +76,69 @@ public IServiceProvider CreateServiceProvider(IServiceCollection services) return jobHostServices.BuildServiceProvider(); } + private void ShimBreakingChanges(IServiceCollection services, ILogger logger) + { + ShimActivatorUtilitiesConstructorAttributeTypes(services, logger); + ShimLegacyNetWorkerMetadataProvider(services, logger); + } + + /// + /// Versions 1.3.0 and older of Microsoft.Azure.Functions.Worker.Sdk register a type with a constructor that + /// takes a strign parameter. This workeed with DryIoc, but not with the new DI container. + /// Shimming those types to provide backwards compatibility, but this should be removed in the future. + /// + /// The to inspect and modify. + private static void ShimLegacyNetWorkerMetadataProvider(IServiceCollection services, ILogger logger) + { + const string functionProviderTypeName = "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.JsonFunctionProvider, Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; + const string jsonReaderTypeName = "Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader.FunctionMetadataJsonReader, Microsoft.Azure.WebJobs.Extensions.FunctionMetadataLoader, Version=1.0.0.0, Culture=neutral, PublicKeyToken=551316b6919f366c"; + + Type functionProviderType = Type.GetType(functionProviderTypeName); + if (functionProviderType is null) + { + return; + } + + Type jsonReaderType = Type.GetType(jsonReaderTypeName); + var constructor = functionProviderType.GetConstructor([jsonReaderType, typeof(string)]); + if (constructor is null) + { + return; + } + + ServiceDescriptor descriptorToShim = null; + foreach (ServiceDescriptor descriptor in services) + { + if (descriptor.ImplementationType == functionProviderType) + { + logger.LogInformation("Shimming .NET Worker Function Provider constructor for {ImplementationType}.", descriptor.ImplementationType); + descriptorToShim = descriptor; + break; + } + } + + if (descriptorToShim is not null) + { + var newDescriptor = ServiceDescriptor.Describe( + descriptorToShim.ServiceType, + sp => + { + var jsonReader = sp.GetRequiredService(jsonReaderType); + return constructor.Invoke([jsonReader, null]); + }, + descriptorToShim.Lifetime); + + services.Remove(descriptorToShim); + services.Add(newDescriptor); + } + } + /// /// .NET 8 has a breaking change regarding no longer functioning as expected. /// We have some known extension types which are impacted by this. To avoid a regression, we are manually shimming those types. /// /// The service collection. - private void ShimBreakingChange(IServiceCollection services) + private static void ShimActivatorUtilitiesConstructorAttributeTypes(IServiceCollection services, ILogger logger) { Dictionary toReplace = null; static bool HasPreferredCtor(Type type) @@ -105,7 +162,7 @@ bool TryCreateReplacement(ServiceDescriptor descriptor, out ServiceDescriptor re return false; } - _logger.LogInformation("Shimming DI constructor for {ImplementationType}.", descriptor.ImplementationType); + logger.LogInformation("Shimming DI constructor for {ImplementationType}.", descriptor.ImplementationType); ObjectFactory factory = ActivatorUtilities.CreateFactory(descriptor.ImplementationType, Type.EmptyTypes); replacement = ServiceDescriptor.Describe(