Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Castle.Windsor.Extensions.DependencyInjection No Scope Available #595

Open
EricMKaufman opened this issue Apr 5, 2021 · 4 comments
Open

Comments

@EricMKaufman
Copy link

EricMKaufman commented Apr 5, 2021

I am using this nuget package to configure Castle Windsor as the DI container. https://www.nuget.org/packages/Castle.Windsor.Extensions.DependencyInjection/

This works fine except for when I try to resolve scoped classes with an IHttpHandler. I get the error: InvalidOperationException: No scope available

What is the correct way to create scope for an HttpHandler?

Here's a sample application reproducing the issue:

namespace WebApplication1
{
    public class Global : System.Web.HttpApplication
    {
        public static IWindsorContainer Container;

        protected void Application_Start(object sender, EventArgs e)
        {
            Console.WriteLine("Hello World!");

            Container = new WindsorContainer();
            var host = new HostBuilder()
                .UseWindsorContainerServiceProvider(Container)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddScoped<SomeService>();
                })
                .Build();

            //Note: This works great
            var resolved = Container.Resolve<SomeService>();
        }

        protected void Session_Start(object sender, EventArgs e)
        {

        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {

        }

        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {

        }

        protected void Application_Error(object sender, EventArgs e)
        {

        }

        protected void Session_End(object sender, EventArgs e)
        {

        }

        protected void Application_End(object sender, EventArgs e)
        {

        }
    }
    
    public class SomeService
    {
  
    }
    public class HttpHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            using (var scope = Global.Container.BeginScope())
            {
                //this errors
                var someService = Global.Container.Resolve<SomeService>();
            }
        }

        public bool IsReusable => false;
    }

}

Web.config:

<system.webServer>
		<handlers>
			<add verb="*" path="*" type="WebApplication1.HttpHandler, WebApplication1, Version=1.0.0.0, Culture=neutral" name="default" />
		</handlers>
	</system.webServer>
@Z10yTap0k
Copy link

Z10yTap0k commented Apr 6, 2021

I have related issue:

I use IHostedService. all services registered with ConfigureContainer extension of IHostBuilder.
SimpleSingletone registered as Singleton lifestyle. Service IAnyScopedService as Scoped

public class SimpleSingletone
{
       private readonly IServiceScopeFactory  _serviceScopeFactory;
       public SimpleSingletone(IServiceScopeFactory serviceScopeFactory)
       {
           _serviceScopeFactory = serviceScopeFactory ?? throw new ArgumentNullException(nameof(IServiceScopeFactory ))
       }
       public string Get()
       {
          var scope = _serviceScopeFactory.CreateScope();
          try
          {
              var scopedService = scope.ServiceProvider.GetService<IAnyScopedService>();
              return scopedService.Get(userId);
          }
          finally
          {
              scope.Dispose();
          }
       }
}

scope.ServiceProvider.GetService<> was throw No Scope.

if i replace IServiceScopeFactory IWindsorContainer all works perfectly.

@EricMKaufman
Copy link
Author

EricMKaufman commented Apr 6, 2021

I was able to come up with a work around, but I think it's a bit of a hack and I'm unsure how it will effect releasing scoped resources.

The below creates a class that derives from WindsorServiceProviderFactoryBase so it can create a root scope on the executing thread.

namespace WebApplication1
{
    public class Global : System.Web.HttpApplication
    {
        public static WindsorServiceProviderFactoryCustom ServiceProviderFactory;

        protected void Application_Start(object sender, EventArgs e)
        {
            Console.WriteLine("Hello World!");

            var container = new WindsorContainer();

            ServiceProviderFactory = new WindsorServiceProviderFactoryCustom(container);
    

            var host = new HostBuilder()
                .UseWindsorContainerServiceProvider(ServiceProviderFactory)
                .ConfigureServices((hostContext, services) =>
                {
                    services.AddScoped<SomeService>();
                })
                .Build();

            //Note: This works great
            var resolved = ServiceProviderFactory.Container.Resolve<SomeService>();
        }

        protected void Session_Start(object sender, EventArgs e)
        {

        }

        protected void Application_BeginRequest(object sender, EventArgs e)
        {

        }

        protected void Application_AuthenticateRequest(object sender, EventArgs e)
        {

        }

        protected void Application_Error(object sender, EventArgs e)
        {

        }

        protected void Session_End(object sender, EventArgs e)
        {

        }

        protected void Application_End(object sender, EventArgs e)
        {

        }
    }
    
    public class SomeService
    {
    }

    public class HttpHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext context)
        {
            //This fixes the error, but I am too unfamiliar with the internals of this library to understand if this is a reasonable workaround. Will the component be scoped to `scope` below?
            Global.ServiceProviderFactory.CreateRootScope();

            using (var scope = Global.ServiceProviderFactory.Container.BeginScope())
            {
                //this no longer errors, but at what cost.
                var someService = Global.ServiceProviderFactory.Container.Resolve<SomeService>();
            }
        }

        public bool IsReusable => false;
    }



    public sealed class WindsorServiceProviderFactoryCustom : WindsorServiceProviderFactoryBase
    {
        public WindsorServiceProviderFactoryCustom(IWindsorContainer container)
        {
            CreateRootScope();
            SetRootContainer(container);
        }
        
        public new void CreateRootScope()
        {
            base.CreateRootScope();
        }
    }

}

@ltines
Copy link
Contributor

ltines commented Jul 23, 2021

I think this has been fixed as CreateRootScope is called in WindsorServiceProviderFactory constructor.

I think the cleanest way to do it though is:

  • make host static variable
  • access services via host.Services
using (var scope = host.Services.CreateScope())
{
    scope.GetService<SomeService>();
}

@lmorvan
Copy link

lmorvan commented Jan 12, 2024

I have this issue with the latest versions of Castle.Windsor 6.0.0 and Castle.Windsor.Extensions.Hosting 6.0.0.
I call this code on my ASP.NET 6.0 server:

var builder = WebApplication.CreateBuilder();
var container = new WindsorContainer();
builder.Host.UseWindsorContainerServiceProvider(container);

And when running my server (with gRPC services, and a Yarp reverse proxy configured), I get this error:

An unhandled exception has occurred while executing the request.
      System.InvalidOperationException: No scope available
         at Castle.Windsor.Extensions.DependencyInjection.Scope.ExtensionContainerScopeCache.get_Current()
         at Castle.Windsor.Extensions.DependencyInjection.Scope.ForcedScope..ctor(ExtensionContainerScopeBase scope)
         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.Routing.Matching.DfaMatcherFactory.CreateMatcher(EndpointDataSource dataSource)
         at Microsoft.AspNetCore.Routing.EndpointRoutingMiddleware.InitializeCoreAsync()
      --- End of stack trace from previous location ---

I have checked the method CreateRootScope is indeed called in the underlying WindsorServiceProviderFactory.

Any idea? 😄

Addendum: The exact same code used to work with the previous version / package Caste.Windsor.Extensions.DependencyInjection 5.1.1.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants