OwinWebRequestScopeAccessor

OwinWebRequestScopeAccessor

本文介绍了如何在OWIN中使用温莎城堡的PerWebRequest生活方式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在将现有的ASP .Net Web API 2项目转换为使用OWIN.该项目使用Castle Windsor作为依赖项注入框架,并设置了其中一种依赖项以使用PerWebRequest生活方式.

I am converting an existing ASP .Net Web API 2 project to use OWIN. The project uses Castle Windsor as the dependency injection framework with one of the dependencies set to use the PerWebRequest lifestyle.

当我向服务器发出请求时,出现了Castle.MicroKernel.ComponentResolutionException异常.异常建议将以下内容添加到配置文件中的system.web/httpModulessystem.WebServer/modules部分:

When I make a request to the server I get a Castle.MicroKernel.ComponentResolutionException exception. The exception recommends adding the following to the system.web/httpModules and system.WebServer/modules sections in the config file:

<add name="PerRequestLifestyle"
     type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />

这不能解决错误.

从SimpleInjector的OWIN集成提供的示例中汲取灵感,我尝试使用以下方法在OWIN启动类中设置范围(以及更新依赖项的生活方式):

Taking inspiration from the example provided by SimpleInjector's OWIN integration, I attempted to set a scope in the OWIN startup class (as well as update the dependency's lifestyle) using:

appBuilder.User(async (context, next) =>
{
    using (config.DependencyResolver.BeginScope()){
    {
        await next();
    }
}

不幸的是,这也不起作用.

Unfortunately this hasn't worked either.

如何使用温莎城堡的PerWebRequest生活方式或在OWIN中进行模拟?

How can I use Castle Windsor's PerWebRequest lifestyle or simulate it in OWIN?

推荐答案

根据Castle Windsor文档,您可以实现自己的自定义范围.您必须实现Castle.MicroKernel.Lifestyle.Scoped.IScopeAccessor接口.

According to the Castle Windsor documentation you can implement your own custom scope. You have to implement the Castle.MicroKernel.Lifestyle.Scoped.IScopeAccessor interface.

然后在注册组件时指定作用域访问器:

You then specify your scope accessor when registering your component:

Container.Register(Component.For<MyScopedComponent>().LifestyleScoped<OwinWebRequestScopeAccessor >());

OwinWebRequestScopeAccessor实现了Castle.Windsor的IScopeAccessor:

The class OwinWebRequestScopeAccessor implements Castle.Windsor's IScopeAccessor:

using Castle.MicroKernel.Context;
using Castle.MicroKernel.Lifestyle.Scoped;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using System.Threading.Tasks;

namespace Web.Api.Host
{
    public class OwinWebRequestScopeAccessor : IScopeAccessor
    {
        public void Dispose()
        {
            var scope = PerWebRequestLifestyleOwinMiddleware.YieldScope();
            if (scope != null)
            {
                scope.Dispose();
            }
        }

        public ILifetimeScope GetScope(CreationContext context)
        {
            return PerWebRequestLifestyleOwinMiddleware.GetScope();
        }
    }
}

如您所见,OwinWebRequestScopeAccessor将调用委派给 GetScope ,并将处置委派给PerWebRequestLifestyleOwinMiddleware.

As you can see OwinWebRequestScopeAccessor delegates the calls to GetScope and Dispose to PerWebRequestLifestyleOwinMiddleware.

PerWebRequestLifestyleOwinMiddleware是温莎城堡的ASP.NET IHttpModule的OWIN计数器部分 PerWebRequestLifestyleModule .

The class PerWebRequestLifestyleOwinMiddleware is the OWIN counter part of Castle Windsor's ASP.NET IHttpModule PerWebRequestLifestyleModule.

这是PerWebRequestLifestyleOwinMiddleware类:

using Castle.MicroKernel;
using Castle.MicroKernel.Lifestyle.Scoped;
using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Web.Api.Host
{
    using AppFunc = Func<System.Collections.Generic.IDictionary<string, object>, System.Threading.Tasks.Task>;

    public class PerWebRequestLifestyleOwinMiddleware
    {
        private readonly AppFunc _next;
        private const string c_key = "castle.per-web-request-lifestyle-cache";
        private static bool _initialized;

        public PerWebRequestLifestyleOwinMiddleware(AppFunc next)
        {
            _next = next;
        }

        public async Task Invoke(IDictionary<string, object> environment)
        {
            var requestContext = OwinRequestScopeContext.Current;
            _initialized = true;

            try
            {
                await _next(environment);
            }
            finally
            {
                var scope = GetScope(requestContext, createIfNotPresent: false);
                if (scope != null)
                {
                    scope.Dispose();
                }
                requestContext.EndRequest();
            }
        }

        internal static ILifetimeScope GetScope()
        {
            EnsureInitialized();
            var context = OwinRequestScopeContext.Current;
            if (context == null)
            {
                throw new InvalidOperationException(typeof(OwinRequestScopeContext).FullName +".Current is null. " +
                    typeof(PerWebRequestLifestyleOwinMiddleware).FullName +" can only be used with OWIN.");
            }
            return GetScope(context, createIfNotPresent: true);
        }

        /// <summary>
        /// Returns current request's scope and detaches it from the request
        /// context. Does not throw if scope or context not present. To be
        /// used for disposing of the context.
        /// </summary>
        /// <returns></returns>
        internal static ILifetimeScope YieldScope()
        {
            var context = OwinRequestScopeContext.Current;
            if (context == null)
            {
                return null;
            }
            var scope = GetScope(context, createIfNotPresent: false);
            if (scope != null)
            {
                context.Items.Remove(c_key);
            }
            return scope;
        }

        private static void EnsureInitialized()
        {
            if (_initialized)
            {
                return;
            }
            throw new ComponentResolutionException("Looks like you forgot to register the OWIN middleware " + typeof(PerWebRequestLifestyleOwinMiddleware).FullName);
        }

        private static ILifetimeScope GetScope(IOwinRequestScopeContext context, bool createIfNotPresent)
        {
            ILifetimeScope candidates = null;
            if (context.Items.ContainsKey(c_key))
            {
                candidates = (ILifetimeScope)context.Items[c_key];
            }
            else if (createIfNotPresent)
            {
                candidates = new DefaultLifetimeScope(new ScopeCache());
                context.Items[c_key] = candidates;
            }
            return candidates;
        }
    }

    public static class AppBuilderPerWebRequestLifestyleOwinMiddlewareExtensions
    {
        /// <summary>
        /// Use <see cref="PerWebRequestLifestyleOwinMiddleware"/>.
        /// </summary>
        /// <param name="app">Owin app.</param>
        /// <returns></returns>
        public static IAppBuilder UsePerWebRequestLifestyleOwinMiddleware(this IAppBuilder app)
        {
            return app.Use(typeof(PerWebRequestLifestyleOwinMiddleware));
        }
    }
}

Castle Windsor的ASP.NET IHttpModule PerWebRequestLifestyleModule利用HttpContext.Current来基于每个Web请求存储Castles Windsor ILifetimeScope. PerWebRequestLifestyleOwinMiddleware类使用OwinRequestScopeContext.Current.这基于 Yoshifumi Kawai 的想法.

Castle Windsor's ASP.NET IHttpModule PerWebRequestLifestyleModule utilizes HttpContext.Current for storing the Castle Windsor ILifetimeScope on a per-web-request basis. PerWebRequestLifestyleOwinMiddleware class uses OwinRequestScopeContext.Current. This is based on the idea of Yoshifumi Kawai.

以下OwinRequestScopeContext的实现是我对Yoshifumi Kawai原始的OwinRequestScopeContext的轻量级实现.

The implementation of OwinRequestScopeContext below is my lightweight implementation of Yoshifumi Kawai's original OwinRequestScopeContext.

注意:如果您不希望这种轻量级实现,可以在NuGet软件包管理器控制台中运行以下命令来使用Yoshifumi Kawai出色的原始实现:

Note: if you don't want this lightweight implementation you can use Yoshifumi Kawai's excellent original implementation by running this command in the NuGet Package Manager Console:

PM> Install-Package OwinRequestScopeContext

OwinRequestScopeContext的轻量级实现:

using System;
using System.Collections.Concurrent;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Remoting.Messaging;
using System.Text;
using System.Threading.Tasks;

namespace Web.Api.Host
{
    public interface IOwinRequestScopeContext
    {
        IDictionary<string, object> Items { get; }
        DateTime Timestamp { get; }
        void EndRequest();
    }

    public class OwinRequestScopeContext : IOwinRequestScopeContext
    {
        const string c_callContextKey = "owin.reqscopecontext";
        private readonly DateTime _utcTimestamp = DateTime.UtcNow;
        private ConcurrentDictionary<string, object> _items = new ConcurrentDictionary<string, object>();

        /// <summary>
        /// Gets or sets the <see cref="IOwinRequestScopeContext"/> object
        /// for the current HTTP request.
        /// </summary>
        public static IOwinRequestScopeContext Current
        {
            get
            {
                var requestContext = CallContext.LogicalGetData(c_callContextKey) as IOwinRequestScopeContext;
                if (requestContext == null)
                {
                    requestContext = new OwinRequestScopeContext();
                    CallContext.LogicalSetData(c_callContextKey, requestContext);
                }
                return requestContext;
            }
            set
            {
                CallContext.LogicalSetData(c_callContextKey, value);
            }
        }

        public void EndRequest()
        {
            CallContext.FreeNamedDataSlot(c_callContextKey);
        }

        public IDictionary<string, object> Items
        {
            get
            {
                return _items;
            }
        }

        public DateTime Timestamp
        {
            get
            {
                return _utcTimestamp.ToLocalTime();
            }
        }
    }
}

当所有部件准备就绪时,您可以将它们捆绑在一起.在OWIN启动类中,调用appBuilder.UsePerWebRequestLifestyleOwinMiddleware();扩展方法以注册上面定义的OWIN中间件.在appBuilder.UseWebApi(config);之前执行此操作:

When you have all the pieces in place you can tie things up. In your OWIN startup class call the appBuilder.UsePerWebRequestLifestyleOwinMiddleware(); extension method to register the OWIN middle ware defined above. Do this before appBuilder.UseWebApi(config);:

using Owin;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Web.Http;
using System.Diagnostics;
using Castle.Windsor;
using System.Web.Http.Dispatcher;
using System.Web.Http.Tracing;

namespace Web.Api.Host
{
    class Startup
    {
        private readonly IWindsorContainer _container;

        public Startup()
        {
            _container = new WindsorContainer().Install(new WindsorInstaller());
        }

        public void Configuration(IAppBuilder appBuilder)
        {
            var properties = new Microsoft.Owin.BuilderProperties.AppProperties(appBuilder.Properties);
            var token = properties.OnAppDisposing;
            if (token != System.Threading.CancellationToken.None)
            {
                token.Register(Close);
            }

            appBuilder.UsePerWebRequestLifestyleOwinMiddleware();

            //
            // Configure Web API for self-host.
            //
            HttpConfiguration config = new HttpConfiguration();
            WebApiConfig.Register(config);
            appBuilder.UseWebApi(config);
        }

        public void Close()
        {
            if (_container != null)
                _container.Dispose();
        }
    }
}

示例WindsorInstaller类展示了如何使用OWIN每个Web请求范围:

The sample WindsorInstaller class shows how you can use the OWIN per-web-request scope:

using Castle.MicroKernel.Registration;
using Castle.MicroKernel.SubSystems.Configuration;
using Castle.Windsor;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace Web.Api.Host
{
    class WindsorInstaller : IWindsorInstaller
    {
        public void Install(IWindsorContainer container, IConfigurationStore store)
        {
            container.Register(Component
                .For<IPerWebRequestDependency>()
                .ImplementedBy<PerWebRequestDependency>()
                .LifestyleScoped<OwinWebRequestScopeAccessor>());

            container.Register(Component
                .For<Controllers.V1.TestController>()
                .LifeStyle.Transient);
        }
    }
}

我在上面提出的解决方案是基于现有的/src/Castle.Windsor/MicroKernel/Lifestyle/PerWebRequestLifestyleModule.cs和川义芳纯正的OwinRequestScopeContext.

The solution I have laid out above is based on the existing /src/Castle.Windsor/MicroKernel/Lifestyle/PerWebRequestLifestyleModule.cs and Yoshifumi Kawai'soriginal OwinRequestScopeContext.

这篇关于如何在OWIN中使用温莎城堡的PerWebRequest生活方式的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

08-24 01:00