Windsor: NET 6.0 ForcedScope Failed with "No Available Scope" on GET request
First of all, great work all of you. I’m trying to port some project of mine to NET 6.0 and AspNetCore, which are new to me. I’ve downloaded master, and using the Castle.Windsor.Extensions.Dependency.Injection. When I run a simple Hello World I’m facing a “No Scope Available exception”.
My initialization code is pretty simple, just trying to put all together:
private static void Main(string[] args)
{
IApplicationInitializer initializer;
var builder = WebApplication.CreateBuilder(args);
builder.Host.UseTaxologicCoreInitialization("WebPortal", out initializer);
initializer.Initialize();
var app = builder.Build();
app.MapGet("/", () => "Hello World!");
app.Run();
}
IAplicationInitializer implementation does all the windsor registration.
UseTaxologicCoreInitialization is just
public static IHostBuilder UseTaxologicCoreInitialization(this IHostBuilder hostBuilder, string _webSiteName, out IApplicationInitializer initializer)
{
var ioc = new WindsorContainer();
var f = new WindsorServiceProviderFactory(ioc);
ioc.Register(Component.For<IHostBuilder>().Instance(hostBuilder));
initializer = new WindsorApplicationInitializer(ioc, new AppPathFinder(), _webSiteName);
return hostBuilder.UseWindsorContainerServiceProvider(f);
}
If I run this code, it fails at app.Run() when it gets it first request to / with an exception “No Scope Available” at line 27 of ExtensionContainerScopeCache , that comes fron ForcedScope constructor.
For what I can infere from the code, as I’m no expert in Castle internals, the ForcedScope stashes the current Scope, set as current the passed one and restores it on ForcedSope disposal. The problem is that when there is no current scope in the cache it fails when tries to get it to set previousScope in
internal ForcedScope(ExtensionContainerScopeBase scope)
{
previousScope = ExtensionContainerScopeCache.Current;
this.scope = scope;
ExtensionContainerScopeCache.Current = scope;
}
I did a couple of small changes to prevent that, and the exception has gone. These are the changes so you can evaluate them and include if you think that has value:
in ExtensionContainerScopeCache I added a getter to know if Current has value
internal static bool hasContext
{
get => current.Value is not null;
}
in ForcedScope I changed the Constructor with this
internal ForcedScope(ExtensionContainerScopeBase scope)
{
if (ExtensionContainerScopeCache.hasContext)
previousScope = ExtensionContainerScopeCache.Current;
else
previousScope = null;
this.scope = scope;
ExtensionContainerScopeCache.Current = scope;
}
It just prevent the exception if there is no current scope
Looking forward to hear your comments about the changes, or any comments on how to prevent the exception by other means
Thanks in advance
Fernando
About this issue
- Original URL
- State: open
- Created a year ago
- Reactions: 1
- Comments: 17 (7 by maintainers)
Commits related to this issue
- Refs: #646 Castle.Windsor.Extension.DepencencyInjection removed null check on scope cache (AsyncLocal can be null on Threads coming from Threadpool.UnsafeQueueUserWorkItem, having no null check was a... — committed to AGiorgetti/Windsor by AGiorgetti a year ago
- Fix #646: fix scope and scope-accessor implementation To fix issue #563 (support concurrently existing containers) a fix was created in #577. After the initial fix some refactorings were done on the... — committed to rvdginste/Windsor by rvdginste a year ago
- Refs: #646 Castle.Windsor.Extension.DepencencyInjection removed null check on scope cache (AsyncLocal can be null on Threads coming from Threadpool.UnsafeQueueUserWorkItem, having no null check was a... — committed to rvdginste/Windsor by AGiorgetti a year ago
- Test for #646: asp.net core 6 app causes null reference in scope — committed to rvdginste/Windsor by rvdginste a year ago
- Test for #646: resolve from thread pool causes null reference in scope — committed to rvdginste/Windsor by AGiorgetti a year ago
- Fix #646: fix scope and scope-accessor implementation To fix issue #563 (support concurrently existing containers) a fix was created in #577. After the initial fix some refactorings were done on the... — committed to rvdginste/Windsor by rvdginste a year ago
- Test for #646: asp.net core 6 app causes null reference in scope — committed to spectraqest/Windsor by rvdginste a year ago
- Test for #646: resolve from thread pool causes null reference in scope — committed to spectraqest/Windsor by AGiorgetti a year ago
- Fix #646: fix scope and scope-accessor implementation To fix issue #563 (support concurrently existing containers) a fix was created in #577. After the initial fix some refactorings were done on the... — committed to spectraqest/Windsor by rvdginste a year ago
I was involved with the original change for #577 (initial commit was a minimal change in Extensions.DI and was related to the root scope, not clear what changes were done afterwards) and will look at.
If someone can add a unit test that exposes the bug, that would be super useful!
Here are a couple more cases that can fail:
Trying to resolve (with the underlying Castle Windsor) something that has a lifestyle that kicks in any scope accessor will result in the usual AsyncLocal problem.
It’s a pretty ugly case but in my application it happens because of different “adapters” for dependency injection that use the same Castle Windsor container (AspNetCore and Akka, to be more specific, but may happens in many other scenario).
Trying to create (or resolve) a Service Provider in an Unsafe Thread has the same problem.
EDIT: the typical problematic scenario is like this:
Hi, I already had a look at it this weekend using the original commits on the branch (commits were squashed before merge) and from my tests it seems the initial commits were fine and the bug was introduced in later commits (as part of the same change). #577 was a fix for #563 and I’d really like to implement a fix instead of reverting everything.
My next steps are to first add an extra unit test. I could reproduce the error when actually setting up and running a minimal Asp.Net Core app (as reported by @fsalas ), but not without Asp.Net Core. I will add a test that uses the Asp.Net Core testing facility and see if that works for the test case. Then I will work on the fix itself and use the new test as a reference.
Is there a specific reason you need to upgrade to 6.0, or can you hold off for now and stick with 5.1.1/5.1.2? I plan to have a fix for this by the end of the coming weekend. I use this extension myself on several applications that are in active development, so it’s in my own interest to have it fixed sooner rather than later.
Ok, understood. We have reverted our code to 5.1.2 but as soon as I have a time slot, I will do my best to help on this one.
No, I mean this is an open source project that needs contributors.
Hello all
Having similar problem trying to migrate working app from Windsor 5.1.2 -> 6.0. App also targeted to .net6 (same behavior with .net7) Exception happens on 1st request.
Previously in code we have UseWindsorContainerServiceProvider call
According to Windsor src this call must initialize ExtensionContainerScopeCache.Current, from where exception is thrown.
But it remains uninitialized.
Current is stored in AsyncLocal
internal static readonly AsyncLocal<ExtensionContainerScopeBase> current = new AsyncLocal<ExtensionContainerScopeBase>();
So it can be different Async context on initialization and 1st request
But why all works on previous version and do not work on Windsor 6?