gpt4 book ai didi

c# - 如何在 OWIN 中使用 CaSTLe Windsor 的 PerWebRequest 生活方式

转载 作者:太空狗 更新时间:2023-10-29 20:56:23 27 4
gpt4 key购买 nike

我正在将现有的 ASP .Net Web API 2 项目转换为使用 OWIN。该项目使用 CaSTLe Windsor 作为依赖项注入(inject)框架,其中一个依赖项设置为使用 PerWebRequest 生活方式。

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

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

这不能解决错误。

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

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

不幸的是,这也没有用。

如何使用 CaSTLe Windsor 的 PerWebRequest 生活方式或在 OWIN 中模拟它?

最佳答案

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

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

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

OwinWebRequestScopeAccessor 类实现了 CaSTLe.Windsor 的 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 将调用委托(delegate)给 GetScope 并将 Dispose 委托(delegate)给 PerWebRequestLifestyleOwinMiddleware

PerWebRequestLifestyleOwinMiddleware 类是 CaSTLe Windsor 的 ASP.NET IHttpModule 的 OWIN 对应部分 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 请求的基础上存储 CaSTLe Windsor ILifetimeScopePerWebRequestLifestyleOwinMiddleware 类使用 OwinRequestScopeContext.Current。这是基于 Yoshifumi Kawai 的想法.

下面OwinRequestScopeContext的实现是我对Yoshifumi Kawai原版OwinRequestScopeContext的轻量级实现。


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

PM> 安装包 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); 之前执行此操作:

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 范围:

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 Kawai 的原始 OwinRequestScopeContext

关于c# - 如何在 OWIN 中使用 CaSTLe Windsor 的 PerWebRequest 生活方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31412230/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com