JasperFx / lamar

Fast Inversion of Control Tool and Successor to StructureMap
https://jasperfx.github.io/lamar
MIT License
572 stars 119 forks source link

ArgumentException when resolving OptionsInstance #103

Closed CodingGorilla closed 5 years ago

CodingGorilla commented 5 years ago

I think this might be related to #90, but I'm not sure. I see this already has locking on it, but I'm getting a lot of these errors all of a sudden (or noticing them all of a sudden).

System.ArgumentException: An item with the same key has already been added. Key: -1133882944

The stack trace is:

   at bool System.Collections.Generic.Dictionary<TKey, TValue>.TryInsert(TKey key, TValue value, InsertionBehavior behavior)
   at object Lamar.Microsoft.DependencyInjection.OptionsInstance<T>.resolveFromRoot(Scope root)
   at object Lamar.Microsoft.DependencyInjection.OptionsInstance<T>+<>c__DisplayClass1_0.<ToResolver>b__0(?)+(Scope s) => { }
   at Func<Scope, object> Lamar.ServiceGraph.FindResolver(Type serviceType)+(Scope s) => { }
   at object Lamar.IoC.Scope.GetInstance(Type serviceType)
   at object Lamar.IoC.Scope.Microsoft.Extensions.DependencyInjection.ISupportRequiredService.GetRequiredService(Type serviceType)
   at object Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
   at T Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService<T>(IServiceProvider provider)
   at void LaMotte.Api.Startup+<>c.<ConfigureServiceClients>b__8_0(?)+(IServiceProvider sp, HttpClient client) => { } in D:/a/1/s/src/LaMotte.Api/Startup.cs:line 91
   at IHttpClientBuilder Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.ConfigureHttpClient(IHttpClientBuilder builder, Action<HttpClient> configureClient)+(HttpClient client) => { }
   at HttpClient Microsoft.Extensions.Http.DefaultHttpClientFactory.CreateClient(string name)
   at IHttpClientBuilder Microsoft.Extensions.DependencyInjection.HttpClientBuilderExtensions.AddTypedClient<TClient>(IHttpClientBuilder builder)+(IServiceProvider s) => { }
   at ISitesService Jasper.Generated.LaMotte_BusinessLogic_BusinessServices_ISitesService_sitesService.Build(Scope scope)
   at object Lamar.IoC.Resolvers.TransientResolver<T>.Resolve(Scope scope)
   at Func<Scope, object> Lamar.IoC.Instances.GeneratedInstance.ToResolver(Scope topScope)+(Scope scope) => { }
   at object Lamar.IoC.Scope.TryGetInstance(Type serviceType)
   at object Lamar.IoC.Scope.GetService(Type serviceType)
   at object Microsoft.Extensions.DependencyInjection.ActivatorUtilities.GetService(IServiceProvider sp, Type type, Type requiredBy, bool isDefaultParameterRequired)
   at object lambda_method(Closure, IServiceProvider, object[])
   at Func<ControllerContext, object> Microsoft.AspNetCore.Mvc.Controllers.ControllerActivatorProvider.CreateActivator(ControllerActionDescriptor descriptor)+(ControllerContext controllerContext) => { }
   at object Microsoft.AspNetCore.Mvc.Controllers.ControllerFactoryProvider+<>c__DisplayClass5_0.<CreateControllerFactory>g__CreateController|0(?)+CreateController(ControllerContext controllerContext)
   at Task Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.Next(ref State next, ref Scope scope, ref object state, ref bool isCompleted)
   at async Task Microsoft.AspNetCore.Mvc.Internal.ControllerActionInvoker.InvokeInnerFilterAsync()
   at async Task Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeFilterPipelineAsync()
   at async Task Microsoft.AspNetCore.Mvc.Internal.ResourceInvoker.InvokeAsync()
   at async Task Microsoft.AspNetCore.Builder.RouterMiddleware.Invoke(HttpContext httpContext)
   at async Task LaMotte.Platform.WebApi.LamotteAuthenticationMiddleware.Invoke(HttpContext context)
   at async Task Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.Invoke(HttpContext context)
CodingGorilla commented 5 years ago

My 30 second review indicates that this is probably because another thread has built the same OptionsInstance<> and stored it in the root scope before this options instance finished. Is it worth adding a Scope.Service.TryAdd() that will ignore services that have already been added?

jeremydmiller commented 5 years ago

@CodingGorilla Might be the same symptom, but I"m gonna bet it's a separate fix. I've got time right now to look at it.

jeremydmiller commented 5 years ago

@CodingGorilla I put in a test to try to break this, but I think the extra locking that came in here (https://github.com/JasperFx/lamar/commit/774e47d74e6159f22a4a05eac2d83119d200afa6) did the trick. And yep, you were right, it was related to #90.