Castle.Windsor.Extensions.DependencyInjection parallelism issues
See original GitHub issue@ltines @generik0 thank you for contributing the Castle.Windsor.Extensions.DependencyInjection package. I guess a lot of people are very happy about that!
Since migrating my application to use the ms-di implementation (v5.1), i’ve noticed some potentially serious issues. They occur when running multiple ASP.NET Core Integration Tests in parallel on independent application instances. It seems some dependencies will be shared respectively disposed across HTTP-Request boundaries. At the moment, i can’t say whether this is just a problem when testing or it’s affecting production runtime as well.
My reproducible sample, created using Microsoft guidelines https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests
is attached here InteractionTesting.zip
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args)
.Build()
.Run();
}
public static IHostBuilder CreateHostBuilder(string[] args)
{
return Host.CreateDefaultBuilder(args)
.UseWindsorContainerServiceProvider()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
}
public class Startup
{
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllersWithViews()
.AddControllersAsServices();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app)
{
app.UseDeveloperExceptionPage();
app.UseRouting();
app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); });
}
}
[Route("[controller]")]
public class DemoController : ControllerBase
{
public IActionResult Demo()
{
return Ok();
}
}
As long as the tests are executed serial they work properly… but if you run them in parallel, they fail for different reasons. As an example:
2020-11-18 12:56:56 [Debug] Hosting starting
2020-11-18 12:56:56 [Information] User profile is available. Using '"C:\Users\xxx\AppData\Local\ASP.NET\DataProtection-Keys"' as key repository and Windows DPAPI to encrypt keys at rest.
2020-11-18 12:56:56 [Debug] Found key {04f0ed1c-94e8-451e-958f-4a91811712e6}.
2020-11-18 12:56:56 [Debug] Found key {18c67e43-7aa7-42ad-af20-e4c822e82ca7}.
2020-11-18 12:56:56 [Debug] Found key {735d0b36-36d1-4c23-a002-b11d77f2f72e}.
2020-11-18 12:56:56 [Debug] Found key {844a49aa-69e8-4d4a-a7f5-e1df66e6f6d5}.
2020-11-18 12:56:56 [Debug] Found key {a1f9d835-ee2d-4a32-9169-d9660d9b18ff}.
2020-11-18 12:56:56 [Debug] Found key {c612ea1e-d2e2-4615-884d-11445c357eb5}.
2020-11-18 12:56:56 [Debug] Found key {d96d15f6-6878-420a-8384-a14c380f6d24}.
2020-11-18 12:56:56 [Debug] Found key {e0765c4b-4693-408b-9434-75f3058045d1}.
2020-11-18 12:56:56 [Debug] Considering key {a1f9d835-ee2d-4a32-9169-d9660d9b18ff} with expiration date 2021-02-16 11:46:08Z as default key.
2020-11-18 12:56:56 [Debug] Forwarded activator type request from "Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiXmlDecryptor, Microsoft.AspNetCore.DataProtection, Version=3.1.9.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" to "Microsoft.AspNetCore.DataProtection.XmlEncryption.DpapiXmlDecryptor, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60"
2020-11-18 12:56:56 [Debug] Decrypting secret element using Windows DPAPI.
2020-11-18 12:56:56 [Debug] Forwarded activator type request from "Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Version=3.1.9.0, Culture=neutral, PublicKeyToken=adb9793829ddae60" to "Microsoft.AspNetCore.DataProtection.AuthenticatedEncryption.ConfigurationModel.AuthenticatedEncryptorDescriptorDeserializer, Microsoft.AspNetCore.DataProtection, Culture=neutral, PublicKeyToken=adb9793829ddae60"
2020-11-18 12:56:56 [Debug] Opening CNG algorithm '"AES"' from provider 'null' with chaining mode CBC.
2020-11-18 12:56:56 [Debug] Opening CNG algorithm '"SHA256"' from provider 'null' with HMAC.
2020-11-18 12:56:56 [Debug] Using key {a1f9d835-ee2d-4a32-9169-d9660d9b18ff} as the default key.
2020-11-18 12:56:56 [Debug] Key ring with default key {a1f9d835-ee2d-4a32-9169-d9660d9b18ff} was loaded during application startup.
2020-11-18 12:56:56 [Debug] Loaded hosting startup assembly "AmazingApi"
2020-11-18 12:56:56 [Information] Application started. Press Ctrl+C to shut down.
2020-11-18 12:56:56 [Information] Hosting environment: "Production"
2020-11-18 12:56:56 [Information] Content root path: "C:\Users\xxx\source\repos\InteractionTesting\AmazingApi"
2020-11-18 12:56:56 [Debug] Hosting started
2020-11-18 12:56:56 [Information] Request starting HTTP/1.1 GET http://localhost/demo
2020-11-18 12:56:56 [Debug] 1 candidate(s) found for the request path '"/demo"'
2020-11-18 12:56:56 [Debug] Endpoint '"AmazingApi.DemoController.Demo (AmazingApi)"' with route pattern '"Demo"' is valid for the request path '"/demo"'
2020-11-18 12:56:56 [Debug] Request matched endpoint '"AmazingApi.DemoController.Demo (AmazingApi)"'
2020-11-18 12:56:56 [Information] Executing endpoint '"AmazingApi.DemoController.Demo (AmazingApi)"'
2020-11-18 12:56:56 [Debug] Registered model binder providers, in the following order: ["Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BinderTypeModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ServicesModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.BodyModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.HeaderModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FloatingPointTypeModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.EnumTypeModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.SimpleTypeModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CancellationTokenModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ByteArrayModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FormFileModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.FormCollectionModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.KeyValuePairModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.DictionaryModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ArrayModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.CollectionModelBinderProvider", "Microsoft.AspNetCore.Mvc.ModelBinding.Binders.ComplexTypeModelBinderProvider"]
2020-11-18 12:56:56 [Information] Executed endpoint '"AmazingApi.DemoController.Demo (AmazingApi)"'
2020-11-18 12:56:56 [Error] An unhandled exception has occurred while executing the request.
System.ObjectDisposedException: Cannot access a disposed object.
Object name: 'Scope was already disposed. This is most likely a bug in the calling code.'.
at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.GetScope(CreationContext context)
at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernelByType(CreationContext context, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.ResolveFromKernel(CreationContext context, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.TryResolveCore(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency, Object& value)
at Castle.MicroKernel.Resolvers.DefaultDependencyResolver.Resolve(CreationContext context, ISubDependencyResolver contextHandlerResolver, ComponentModel model, DependencyModel dependency)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.CreateConstructorArguments(ConstructorCandidate constructor, CreationContext context)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.Instantiate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.DefaultComponentActivator.InternalCreate(CreationContext context)
at Castle.MicroKernel.ComponentActivator.AbstractComponentActivator.Create(CreationContext context, Burden burden)
at Castle.MicroKernel.Lifestyle.AbstractLifestyleManager.CreateInstance(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.<>n__0(CreationContext context, Boolean trackedExternally)
at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.<>c__DisplayClass4_0.<Resolve>b__0(Action`1 afterCreated)
at Castle.Windsor.Extensions.DependencyInjection.Scope.ExtensionContainerScope.GetCachedInstance(ComponentModel model, ScopedInstanceActivationCallback createInstance)
at Castle.MicroKernel.Lifestyle.ScopedLifestyleManager.Resolve(CreationContext context, IReleasePolicy releasePolicy)
at Castle.MicroKernel.Handlers.DefaultHandler.ResolveCore(CreationContext context, Boolean requiresDecommission, Boolean instanceRequired, Burden& burden)
at Castle.MicroKernel.Handlers.DefaultHandler.Resolve(CreationContext context, Boolean instanceRequired)
at Castle.MicroKernel.Handlers.AbstractHandler.Resolve(CreationContext context)
at Castle.MicroKernel.DefaultKernel.ResolveComponent(IHandler handler, Type service, Arguments additionalArguments, IReleasePolicy policy, Boolean ignoreParentContext)
at Castle.MicroKernel.DefaultKernel.Castle.MicroKernel.IKernelInternal.Resolve(Type service, Arguments arguments, IReleasePolicy policy, Boolean ignoreParentContext)
at Castle.MicroKernel.DefaultKernel.Resolve(Type service, Arguments arguments)
at Castle.Windsor.WindsorContainer.Resolve(Type service)
at Castle.Windsor.Extensions.DependencyInjection.WindsorScopedServiceProvider.ResolveInstanceOrNull(Type serviceType, Boolean isOptional)
at Castle.Windsor.Extensions.DependencyInjection.WindsorScopedServiceProvider.GetRequiredService(Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService(IServiceProvider provider, Type serviceType)
at Microsoft.Extensions.DependencyInjection.ServiceProviderServiceExtensions.GetRequiredService[T](IServiceProvider provider)
at Microsoft.AspNetCore.Mvc.ViewFeatures.SaveTempDataAttribute.CreateInstance(IServiceProvider serviceProvider)
at Microsoft.AspNetCore.Mvc.Filters.DefaultFilterProvider.ProvideFilter(FilterProviderContext context, FilterItem filterItem)
at Microsoft.AspNetCore.Mvc.Filters.DefaultFilterProvider.OnProvidersExecuting(FilterProviderContext context)
at Microsoft.AspNetCore.Mvc.Filters.FilterFactory.CreateUncachedFiltersCore(IFilterProvider[] filterProviders, ActionContext actionContext, List`1 filterItems)
at Microsoft.AspNetCore.Mvc.Filters.FilterFactory.GetAllFilters(IFilterProvider[] filterProviders, ActionContext actionContext)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerCache.GetCachedResult(ControllerContext controllerContext)
at Microsoft.AspNetCore.Mvc.Infrastructure.ControllerActionInvokerProvider.OnProvidersExecuting(ActionInvokerProviderContext context)
at Microsoft.AspNetCore.Mvc.Infrastructure.ActionInvokerFactory.CreateInvoker(ActionContext actionContext)
at Microsoft.AspNetCore.Mvc.Routing.ActionEndpointFactory.<>c__DisplayClass7_0.<CreateRequestDelegate>b__0(HttpContext context)
at Microsoft.AspNetCore.Routing.EndpointMiddleware.Invoke(HttpContext httpContext)
--- End of stack trace from previous location where exception was thrown ---
at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddleware.Invoke(HttpContext context)
2020-11-18 12:56:56 [Information] Request finished in 16.228ms 500 text/plain
If you stick with the built-in service provider and do not use windsor, it works fine even if the tests will be executed in parallel.
Would you be so kind and take a look at it?
Issue Analytics
- State:
- Created 3 years ago
- Reactions:1
- Comments:24 (23 by maintainers)
Top GitHub Comments
xUnit Workaround: Turn off parallelism inside the assembly
[assembly: CollectionBehavior(DisableTestParallelization = true)]
https://xunit.github.io/docs/running-tests-in-parallel.html