如何使用温莎城堡在范围内的生活方式中注册服务,表现得像 PerWebRequest?

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

我正在使用 OWIN 在自托管网络 api 上工作,我需要使用 Castle Windsor 注册每个范围生活方式的服务。

我知道使用 HttpContext 我可以使用 PerWebRequestHybridPerWebRequestPerThread 生活方式来实现,但是就我而言,我没有 HttpContext。

我创建了以下 owin 中间件,它们将在请求期间执行第一个和最后一个中间件:

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();
        }
    }

当我启动我的应用程序时,我在 IoC 容器中注册了我的服务,如下所示:

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

当我解析 CurrentRequestService 的实例时出现问题,它无法按照范围工作,owin 中间件将在新请求到来时开始它并在它完成时处理它。

能否请您指导我如何注册每个范围的服务,使其在我的应用程序中表现得像 PerWebRequest?

可能有很多范围。

要使用由 SetupMiddleware 控制的范围,您需要在请求处理期间(例如在控制器操作中)解析比 SetupMiddleware 更深的某个地方的 ICurrentRequestService 服务。

因此,SetupMiddleware 将创建范围,然后在控制器操作中 ICurrentRequestService 将在该范围内解析,最后 SetupMiddleware 将通过调用 scope.Dispose().

据我了解,目前你有这样的东西:

// when application starts. Executes once
container.BeginScope(); // the 1st (application) scope beginning. 
container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped());
var service = IoCResolverFactory.GetContainer().Resolve<ICurrentRequestService>();

//in SetupMiddleware. This code executes on every web request
    var container = IoCResolverFactory.GetContainer(); 
    scope = container.BeginScope(); // the request scope beginning

    // here others middlewares 
    // here you can resolve ICurrentRequestService to use the request scope

    scope?.Dispose(); // the request scope end

因此,service 使用应用程序范围而不是请求范围。

正如我所料,我应该定义一个自定义范围以启动并在中间件中进行处理。

我创建了以下自定义作用域访问器:

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;
    }
}

然后我修改了我的 SetupMiddleware 如下:

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();
        }
    }
}

然后我使用上述范围注册了我的服务:

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

PS:

SetupMiddleware 应该是 owin 管道中的第一个中间件。

希望我的代码对其他人有所帮助。