diff --git a/src/WebJobs.Script.WebHost/DependencyInjection/ServiceProviderExtensions.cs b/src/WebJobs.Script.WebHost/DependencyInjection/ServiceProviderExtensions.cs index ff95b12e35..63176f9b43 100644 --- a/src/WebJobs.Script.WebHost/DependencyInjection/ServiceProviderExtensions.cs +++ b/src/WebJobs.Script.WebHost/DependencyInjection/ServiceProviderExtensions.cs @@ -7,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Text; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; @@ -98,11 +99,16 @@ public static IServiceCollection CreateChildContainer(this IServiceProvider serv else if (services.All(s => s.Lifetime == ServiceLifetime.Singleton)) { // We can resolve them from the main container. - var instances = serviceProvider.GetServices(services.Key); + // Materialize services and instances to arrays once. + var servicesArray = services.ToArray(); + var instancesArray = serviceProvider.GetServices(services.Key)?.ToArray() ?? Array.Empty(); - for (var i = 0; i < services.Count(); i++) + // Validate counts + ValidateServiceAndInstanceCounts(servicesArray, instancesArray, services.Key); + + for (var i = 0; i < servicesArray.Length; i++) { - clonedCollection.CloneSingleton(services.ElementAt(i), instances.ElementAt(i)); + clonedCollection.CloneSingleton(servicesArray[i], instancesArray[i]); } } @@ -112,18 +118,22 @@ public static IServiceCollection CreateChildContainer(this IServiceProvider serv // We need a service scope to resolve them. using var scope = serviceProvider.CreateScope(); - var instances = scope.ServiceProvider.GetServices(services.Key); + // Materialize services and instances to arrays once. + var servicesArray = services.ToArray(); + var instancesArray = scope.ServiceProvider.GetServices(services.Key)?.ToArray() ?? Array.Empty(); + + ValidateServiceAndInstanceCounts(servicesArray, instancesArray, services.Key); // Then we only keep singleton instances. - for (var i = 0; i < services.Count(); i++) + for (var i = 0; i < servicesArray.Length; i++) { - if (services.ElementAt(i).Lifetime == ServiceLifetime.Singleton) + if (servicesArray[i].Lifetime == ServiceLifetime.Singleton) { - clonedCollection.CloneSingleton(services.ElementAt(i), instances.ElementAt(i)); + clonedCollection.CloneSingleton(servicesArray[i], instancesArray[i]); } else { - clonedCollection.Add(services.ElementAt(i)); + clonedCollection.Add(servicesArray[i]); } } } @@ -131,6 +141,22 @@ public static IServiceCollection CreateChildContainer(this IServiceProvider serv return clonedCollection; } + + private static void ValidateServiceAndInstanceCounts(TService[] servicesArray, TInstance[] instancesArray, Type serviceType) + { + if (servicesArray.Length != instancesArray.Length) + { + var messageBuilder = new StringBuilder() + .AppendLine($"Mismatch detected for type {serviceType}:") + .AppendLine($"services.Count = {servicesArray.Length}, instances.Count = {instancesArray.Length}") + .Append("Services:\n\t") + .Append(string.Join(",\n\t", servicesArray.Select(s => s.ToString()))) + .Append("\nInstances:\n\t") + .AppendLine(string.Join(",\n\t", instancesArray.Select(i => i?.GetType()?.ToString() ?? "null"))); + Console.WriteLine(messageBuilder.ToString()); + throw new InvalidOperationException(messageBuilder.ToString()); + } + } } internal static class ServiceCollectionExtensions