首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >如何通过OWIN使用城堡温莎的PerWebRequest生活方式

如何通过OWIN使用城堡温莎的PerWebRequest生活方式
EN

Stack Overflow用户
提问于 2015-07-15 00:25:14
回答 3查看 5.3K关注 0票数 6

我正在转换现有的ASPWebAPI2项目以使用OWIN。该项目使用Castle Windsor作为依赖注入框架,其中一个依赖项设置为使用PerWebRequest生活方式。

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

代码语言:javascript
复制
<add name="PerRequestLifestyle"
     type="Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor" />

这并不能解决错误。

从SimpleInjector的OWIN集成提供的示例中获得灵感,我尝试使用以下命令在OWIN启动类中设置作用域(以及更新依赖项的生活方式):

代码语言:javascript
复制
appBuilder.User(async (context, next) =>
{
    using (config.DependencyResolver.BeginScope()){
    {
        await next();
    }
}

不幸的是,这也没有起作用。

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

EN

回答 3

Stack Overflow用户

发布于 2016-04-22 21:56:24

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

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

代码语言:javascript
复制
Container.Register(Component.For<MyScopedComponent>().LifestyleScoped<OwinWebRequestScopeAccessor >());

OwinWebRequestScopeAccessor类实现Castle.Windsor的IScopeAccessor

代码语言:javascript
复制
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、Dispose,将委托给PerWebRequestLifestyleOwinMiddleware

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

这是PerWebRequestLifestyleOwinMiddleware类:

代码语言:javascript
复制
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));
        }
    }
}

城堡温莎的ASP.NET IHttpModule PerWebRequestLifestyleModule利用HttpContext.Current在每个web请求的基础上存储城堡温莎ILifetimeScopePerWebRequestLifestyleOwinMiddleware类使用OwinRequestScopeContext.Current。这是基于Yoshifumi Kawai的思想。

下面的OwinRequestScopeContext实现是我对川井芳美最初的OwinRequestScopeContext的轻量级实现。

注意:如果您不需要这种轻量级实现,您可以通过在NuGet包管理器控制台中运行以下命令来使用Yoshifumi优秀的原始实现:

PM> Install-Package OwinRequestScopeContext

OwinRequestScopeContext的轻量级实现

代码语言:javascript
复制
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);之前执行此操作

代码语言:javascript
复制
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 per-web-request作用域:

代码语言:javascript
复制
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和Yoshifumi的‘soriginal OwinRequestScopeContext

票数 8
EN

Stack Overflow用户

发布于 2017-06-02 18:14:24

我尝试实现Johan Boonstra's answer,但是发现一旦我们使用到ASP.NET的MVC控制器方法,它就不能工作了。

这里有一个更简单的解决方案:

首先,创建一些位于管道开头的Owin中间件并创建一个DefaultLifetimeScope

代码语言:javascript
复制
public class WebRequestLifestyleMiddleware : OwinMiddleware
{
    public const string EnvironmentKey = "WindsorOwinScope";

    public WebRequestLifestyleMiddleware(OwinMiddleware next) : base(next)
    {
    }

    public override async Task Invoke(IOwinContext context)
    {
        ILifetimeScope lifetimeScope = new DefaultLifetimeScope();
        context.Environment[EnvironmentKey] = lifetimeScope;
        try
        {
            await this.Next.Invoke(context);
        }
        finally
        {
            context.Environment.Remove(EnvironmentKey);
            lifetimeScope.Dispose();
        }
    }
}

将其插入到启动配置中的管道开头:

代码语言:javascript
复制
public void Configure(IAppBuilder appBuilder)
{
    appBuilder.Use<WebRequestLifestyleMiddleware>();

    //
    // Further configuration
    //
}

现在创建一个实现IScopeAccessor的类,并获取WebRequestLifestyleMiddleware推送到环境中的作用域:

代码语言:javascript
复制
public class OwinWebRequestScopeAccessor : IScopeAccessor
{
    void IDisposable.Dispose() { }

    ILifetimeScope IScopeAccessor.GetScope(CreationContext context)
    {
        IOwinContext owinContext = HttpContext.Current.GetOwinContext();
        string key = WebRequestLifestyleMiddleware.EnvironmentKey;
        return owinContext.Environment[key] as ILifetimeScope;
    }
}

最后,在注册组件生存期时使用此作用域访问器。例如,我有一个名为AccessCodeProvider的自定义组件,我想在一个请求中重用它:

代码语言:javascript
复制
container.Register(
      Component.For<AccessCodeProvider>()
               .LifestyleScoped<OwinRequestScopeAccessor>()
);

在这种情况下,AccessCodeProvider将在请求中第一次被请求时被创建,然后在整个web请求中重用,最后在WebRequestLifestyleMiddleware调用lifetimeScope.Dispose()时被处理。

票数 6
EN

Stack Overflow用户

发布于 2018-04-24 19:29:23

我创建了PerScope lifestyle,在一个web api应用程序中表现得像PerWebRequest,它使用owin中间件和城堡windsor作为应用程序的IoC。

首先,让我们将windsor容器设置为web api应用程序的IoC,如下所示:

代码语言:javascript
复制
    public class WindsorHttpDependencyResolver : IDependencyResolver
    {
        private readonly IWindsorContainer container;

        public WindsorHttpDependencyResolver(IWindsorContainer container)
        {
            if (container == null)
            {
                throw new ArgumentNullException("container");
            }
            this.container = container;
        }

        public object GetService(Type t)
        {
            return this.container.Kernel.HasComponent(t)
             ? this.container.Resolve(t) : null;
        }

        public IEnumerable<object> GetServices(Type t)
        {
            return this.container.ResolveAll(t).Cast<object>().ToArray();
        }

        public IDependencyScope BeginScope()
        {
            return new WindsorDependencyScope(this.container);
        }

        public void Dispose()
        {
        }
    }//end WindsorHttpDependencyResolver 

  public class WindsorDependencyScope : IDependencyScope
    {
        private readonly IWindsorContainer container;
        private readonly IDisposable scope;

        public WindsorDependencyScope(IWindsorContainer container)
        {
            if (container == null)
                throw new ArgumentNullException("container");

            this.container = container;
        }

        public object GetService(Type t)
        {
            return this.container.Kernel.HasComponent(t)
                ? this.container.Resolve(t) : null;
        }

        public IEnumerable<object> GetServices(Type t)
        {
            return this.container.ResolveAll(t).Cast<object>().ToArray();
        }

        public void Dispose()
        {
            this.scope?.Dispose();
        }
    }

然后,在应用程序启动期间,让我们注册它:

代码语言:javascript
复制
container.Register(Component.For<System.Web.Http.Dependencies.IDependencyResolver>().ImplementedBy<WindsorHttpDependencyResolver>().LifestyleSingleton());

现在在第一个中间件(这将是第一个也是最后一个将被执行的中间件)中,让我们在新请求到达我们的web api时开始作用域,并在它结束时处理它,如下所示:

代码语言:javascript
复制
public class StartinMiddleware : OwinMiddleware
{

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


    public override async Task Invoke(IOwinContext context)
    {

        this.Log().Info("Begin request");
        IDisposable scope = null;            
        try
        {
            // here we are using IoCResolverFactory which returns 
            // the instance of IoC container(which will be singleton for the 
            // whole application)
            var ioCResolver= IoCResolverFactory.GetOrCreate();
            //here we are starting new scope
            scope = ioCResolver.BeginScope();

            await Next.Invoke(context);

            this.Log().Info("End request");
        }
        catch (Exception ex)
        { 
          //here you can log exceptions
        }
        finally
        {
            //here we are desposing scope
            scope?.Dispose();
        }
    }
}

IoC工厂的代码如下所示:

代码语言:javascript
复制
public static class IoCResolverFactory
{
    public static IoCResolver iocResolver;

    public static IoCResolver GetOrCreate()
    {
        if (iocResolver != null)
            return iocResolver;

        iocResolver = new IoCResolver();
        return iocResolver;
    }
}// end IoCResolverFactory

public class IoCResolver
{
    private static WindsorContainer container;

    public IoCResolver()
    {
        container = new WindsorContainer();

        container.Register(Component.For<IoCResolver>().Instance(this).LifestyleSingleton());
        container.Register(Component.For<IWindsorContainer>().Instance(container).LifestyleSingleton());
    }


    public IDisposable BeginScope()
    {
        return container.BeginScope();
    }


    public IDisposable GetCurrentScope()
    {
        return Castle.MicroKernel.Lifestyle.Scoped.CallContextLifetimeScope.ObtainCurrentScope();
    }


    public T Resolve<T>()
    {
        return container.Resolve<T>();
    }

    public IList<T> ResolveAll<T>()
    {
        return container.ResolveAll<T>();
    }

    public void Dispose()
    {
        container.Dispose();
    }
}

在启动期间注册您的服务时,您可以按照以下方式注册要按作用域解析的服务:

代码语言:javascript
复制
container.Register(Component.For<ICurrentRequestService>().ImplementedBy<CurrentRequestService>().LifestyleScoped());
票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/31412230

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档