Search code examples
c#owincastle-windsorowin-middleware

How to register services in scoped lifestyle behaving like PerWebRequest using castle windsor?


I am working on self-hosted web api using OWIN and I need to register a service per scope lifestyle using Castle Windsor.

I know that using HttpContext I can achieve that using PerWebRequest or HybridPerWebRequestPerThread lifestyle but in my case I don't have HttpContext.

I have created the following owin middleware which will be the first and the last middleware will be executed during the request :

public class SetupMiddleware : OwinMiddleware
{

    public SetupMiddleware(OwinMiddleware next) : base(next)
    {
        if (next == null)
        {
            throw new ArgumentNullException("next");
        }
    }


    public override async Task Invoke(IOwinContext context)
    {
        IDisposable scope = null;
        try
        {
           //here I am starting new scope
            var container= IoCResolverFactory.GetContainer();
            scope = container.BeginScope();

            await Next.Invoke(context);

        }
        catch (Exception ex)
        {
        }
        finally
        {
            //here I am disposing it
            scope?.Dispose();
        }
    }

And when I Startup my application I register my service inside the IoC container as following:

container.BeginScope();
container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped());

The problem when I resolve instance of CurrentRequestService, it does not work as per scope which owin middleware will begin it when a new request comes and dispose it when it finishes.

Could you please guide me to how can I register services per scope to behave like PerWebRequest in my application?


Solution

  • As I expected that I should define a custom scope to be started and dispose within a middleware .

    I have created the following custom scope accessor:

    public class OwinWebRequestScopeAccessor : IScopeAccessor
    {
        void IDisposable.Dispose() { }
    
        ILifetimeScope IScopeAccessor.GetScope(CreationContext context)
        {
            IOwinContext owinContext = HttpContext.Current.GetOwinContext();
            string key = SetupMiddleware.EnvironmentKey;
            return owinContext.Environment[key] as ILifetimeScope;
        }
    }
    

    Then I have modified my SetupMiddleware as followings:

    public class SetupMiddleware : OwinMiddleware
    {
        public const string EnvironmentKey = "WindsorOwinScope";
    
        public SetupMiddleware(OwinMiddleware next) : base(next)
        {
            if (next == null)
            {
                throw new ArgumentNullException("next");
            }
        }
    
    
        public override async Task Invoke(IOwinContext context)
        {
    
            ILifetimeScope lifetimeScope = new DefaultLifetimeScope();
    
            try
            {
                context.Environment[EnvironmentKey] = lifetimeScope;
    
                await Next.Invoke(context);
            }
            catch (Exception ex)
            {
            }
            finally
            {
                context.Environment.Remove(EnvironmentKey);
                lifetimeScope.Dispose();
            }
        }
    }
    

    Then I registered my service using the above scope :

    container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped<OwinWebRequestScopeAccessor>());
    

    PS:

    SetupMiddleware should be the first middleware in the owin pipeline.

    I hope my code will help others.