question-mark
Stuck on an issue?

Lightrun Answers was designed to reduce the constant googling that comes with debugging 3rd party libraries. It collects links to all the places you might be looking at while hunting down a tough bug.

And, if you’re still stuck at the end, we’re happy to hop on a call to see how we can help out.

NET 6.0 ForcedScope Failed with "No Available Scope" on GET request

See original GitHub issue

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

Issue Analytics

  • State:open
  • Created 6 months ago
  • Reactions:1
  • Comments:17 (7 by maintainers)

github_iconTop GitHub Comments

2reactions
rvdginstecommented, Jul 28, 2023

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!

1reaction
AGiorgetticommented, Aug 8, 2023

Here are a couple more cases that can fail:

		[Fact]
		public async Task Can_Resolve_From_CastleWindsor() {

			var serviceProvider = new ServiceCollection();
			var container = new WindsorContainer();
			var f = new WindsorServiceProviderFactory(container);
			f.CreateBuilder(serviceProvider);

			container.Register(
				// Component.For<IUserService>().ImplementedBy<UserService>().LifestyleNetTransient(),
				Classes.FromThisAssembly().BasedOn<IUserService>().WithServiceAllInterfaces().LifestyleNetStatic()
				);

			IServiceProvider sp = f.CreateServiceProvider(container);

			IUserService actualUserService;
			actualUserService = container.Resolve<IUserService>();
			Assert.NotNull(actualUserService);

			TaskCompletionSource<IUserService> tcs = new TaskCompletionSource<IUserService>();

			ThreadPool.UnsafeQueueUserWorkItem(state => {
				IUserService actualUserService = null;
				try {
					// resolving (with the underlying Castle Windsor, not using Service Provider) with a lifecycle that has an
                                        // accessor that uses something that is AsyncLocal might be troublesome.
                                        // the custom lifecycle accessor will kicks in, but noone assigns the Current scope (which is uninitialized)
					actualUserService = container.Resolve<IUserService>();
					Assert.NotNull(actualUserService);
				}
				catch (Exception ex) {
					tcs.SetException(ex);
					return;
				}
				tcs.SetResult(actualUserService);
			}, null);

			// Wait for the work item to complete.
			var task = tcs.Task;
			IUserService result = await task;
			Assert.NotNull(result);
		}

		[Fact]
		public async Task Can_Resolve_From_ServiceProvider_cretaed_in_UnsafeQueueUserWorkItem() {

			var serviceProvider = new ServiceCollection();
			var container = new WindsorContainer();
			var f = new WindsorServiceProviderFactory(container);
			f.CreateBuilder(serviceProvider);

			container.Register(
				// Component.For<IUserService>().ImplementedBy<UserService>().LifestyleNetTransient(),
				Classes.FromThisAssembly().BasedOn<IUserService>().WithServiceAllInterfaces().LifestyleNetStatic()
				);

			TaskCompletionSource<IUserService> tcs = new TaskCompletionSource<IUserService>();

			ThreadPool.UnsafeQueueUserWorkItem(state => {
				IUserService actualUserService = null;
				try {
					// creating a service provider here will be troublesome too
					IServiceProvider sp = f.CreateServiceProvider(container);

					actualUserService = sp.GetService<IUserService>();
					Assert.NotNull(actualUserService);
				}
				catch (Exception ex) {
					tcs.SetException(ex);
					return;
				}
				tcs.SetResult(actualUserService);
			}, null);

			// Wait for the work item to complete.
			var task = tcs.Task;
			IUserService result = await task;
			Assert.NotNull(result);
		}

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:

  • configure an AspNet application
  • use the Castle Windsor adapter to “replace” the standard Ms DI.
  • configure AspNet to use logging and any other standard feature.
  • register “something” that depends on the Ms extension logger.
  • start a background unsafe thread (like the ones of AspNet Core)
  • try to resolve “something” from the Castle Winsdor container (not from the adapted IServiceProvider) and you’ll get the error
  • the problem happens because the standard aspnet core services will be registered in castle with the scoped lifesyles that use ExtensionContainerRootScopeAccessor or ExtensionContainerScopeAccessor
Read more comments on GitHub >

github_iconTop Results From Across the Web

Issues · castleproject/Windsor
... of Control container available for .NET - Issues · castleproject/Windsor. ... NET 6.0 ForcedScope Failed with "No Available Scope" on GET request...
Read more >
Asp.net core service per request scope in child scopes
It should be request scoped, not scoped to the current container scope. So if you create new scope inside your code explicitly this...
Read more >
NETSDK1045: The current .NET SDK does not support ...
This error occurs when the build tools can't find the version of the .NET SDK that's needed to build a project. This is...
Read more >
NET 6 Core Web App Returns web page not found
I would expect a new app to run out-of-the-box with the default landing page, but instead I get a webpage not found error....
Read more >
How to fix "No .NET SDKs were found." error--VSCode
Ok This fixed the problem for me. I went to the C:\Program Files (x86)\dotnet. and deleted everthing in the folder.
Read more >

github_iconTop Related Medium Post

No results found

github_iconTop Related StackOverflow Question

No results found

github_iconTroubleshoot Live Code

Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start Free

github_iconTop Related Reddit Thread

No results found

github_iconTop Related Hackernoon Post

No results found

github_iconTop Related Tweet

No results found

github_iconTop Related Dev.to Post

No results found

github_iconTop Related Hashnode Post

No results found