Zgodnie z dokumentacją Castle Windsor można zaimplementować własny niestandardowy zakres. Musisz wdrożyć interfejs Castle.MicroKernel.Lifestyle.Scoped.IScopeAccessor
.
Następnie określ zakres akcesor podczas rejestracji komponent:
Container.Register(Component.For<MyScopedComponent>().LifestyleScoped<OwinWebRequestScopeAccessor >());
Klasa OwinWebRequestScopeAccessor
implementuje Castle.Windsor za 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();
}
}
}
Jak widać OwinWebRequestScopeAccessor
delegatów wywołań GetScope i Usunąć do PerWebRequestLifestyleOwinMiddleware
.
Klasa Klasa PerWebRequestLifestyleOwinMiddleware
jest częścią licznika OWIN modułu ASP.NET IHttpModule Castle Windsor PerWebRequestLifestyleModule.
Jest to klasa 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));
}
}
}
zamku Windsor w ASP.NET IHttpModule PerWebRequestLifestyleModule
wykorzystuje HttpContext.Current
do przechowywania zamku Windsor ILifetimeScope
na zasadzie per-web-żądanie. PerWebRequestLifestyleOwinMiddleware
klasa używa OwinRequestScopeContext.Current
. Jest to oparte na idei Yoshifumi Kawai.
Poniżej znajduje się implementacja OwinRequestScopeContext
, która jest moją lekką implementacją oryginalnej wersji Yoshifumi Kawai OwinRequestScopeContext
.
Uwaga: jeśli nie chcesz to lekki realizacji można wykorzystać doskonałą oryginalną realizację Yoshifumi Kawai poprzez uruchomienie tego polecenia w Nuget konsoli Package Manager:
PM> Install-Package OwinRequestScopeContext
Lightweight implementacja OwinRequestScopeContext
:
Gdy masz wszystkie elementy na miejscu, możesz związać rzeczy. W swojej klasie startowej OWIN wywołaj metodę rozszerzenia appBuilder.UsePerWebRequestLifestyleOwinMiddleware();
, aby zarejestrować zdefiniowane wyżej medium OWIN. Zrób to przed 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();
}
}
}
Klasa próbka WindsorInstaller pokazuje w jaki sposób można korzystać z OWIN zakres 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);
}
}
}
Rozwiązanie Mam określonymi powyżej opiera się na istniejących /src/Castle.Windsor/MicroKernel/Lifestyle/PerWebRequestLifestyleModule.cs
i Yoshifumi Kawai'soriginal OwinRequestScopeContext
.
Interesujące - widzę to przechowuje 'OwinRequestScopeContext' w' System.Runtime.Remoting.Messaging.CallContext'. Moją pierwszą myślą było, aby to zapisać w słowniku środowiska Owin. ('OwinContext.Environment'). –
Chociaż po dalszym badaniu, myślę, że widzę powód: 'System.Runtime.Remoting.Messaging.CallContext' ma globalnie dostępne statyczne elementy, ale nie ma bezpiecznego sposobu statycznego pozyskania' OwinContext'. –