NET 6.0 ForcedScope Failed with "No Available Scope" on GET request
See original GitHub issueFirst 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
Issue Analytics
- State:
- Created 6 months ago
- Reactions:1
- Comments:17 (7 by maintainers)
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: