How to use Castle Windsor PerWebRequest style with OWIN

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

When I make a request to the server, I get a Castle.MicroKernel.ComponentResolutionException exception. An exception is to add the following to the system.web/httpModules and system.WebServer/modules sections in the configuration file:

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

This does not resolve the error.

Inspiring the example provided by the SimpleInjector OWIN integration, I tried to set the scope in the OWIN startup class (and also update the dependency lifestyle) using:

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

Unfortunately, this did not work either.

How can I use the Castle Windsor PerWebRequest style or simulate it in OWIN?

+5
source share
2 answers

According to Castle Windsor documentation, you can implement your own area. You must implement the Castle.MicroKernel.Lifestyle.Scoped.IScopeAccessor interface.

Then you indicate your access aspect when registering your component:

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

The OwinWebRequestScopeAccessor class implements 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(); } } } 

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

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

This is the PerWebRequestLifestyleOwinMiddleware class:

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

Windsor Castle ASP.NET IHttpModule PerWebRequestLifestyleModule uses HttpContext.Current to store Castle Windsor ILifetimeScope for each web request. PerWebRequestLifestyleOwinMiddleware class uses the OwinRequestScopeContext.Current . This is based on the idea of Yoshifumi Kawai .

The implementation of the OwinRequestScopeContext below is my easy implementation of the original Yoshifumi Kawai OwinRequestScopeContext .


Note. If you do not want this easy implementation, you can use the excellent original implementation of Yoshifumi Kawai by running this command in the NuGet package manager console:

PM> Install-Package OwinRequestScopeContext


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

When you have all the parts in place, you can knit things together. In your OWIN launch class, call the extension method appBuilder.UsePerWebRequestLifestyleOwinMiddleware(); to register the OWIN middleware 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(); } } } 

An example WindsorInstaller class shows how you can use the OWIN scope for each 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); } } } 

The solution outlined above is based on the existing /src/Castle.Windsor/MicroKernel/Lifestyle/PerWebRequestLifestyleModule.cs and Yoshifumi Kawai'ororinal OwinRequestScopeContext .

+6
source

I tried to execute Johan Boonstra's answer , but found that it does not work after we got the ASP.NET MVC Controller methods.

Here's a simpler solution:

First create some Owin middleware that is at the beginning of the pipeline and will create DefaultLifetimeScope

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

Insert it at the beginning of the pipeline in the launch configuration:

 public void Configure(IAppBuilder appBuilder) { appBuilder.Use<WebRequestLifestyleMiddleware>(); // // Further configuration // } 

Now you create a class that implements IScopeAccessor , and selects the area in which WebRequestLifestyleMiddleware is placed in the environment:

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

Finally, use this access area when recording component lifetime. For example, I have my own custom component called AccessCodeProvider , which I want to reuse within a single request:

 container.Register( Component.For<AccessCodeProvider>() .LifestyleScoped<OwinRequestScopeAccessor>() ); 

In this case, the AccessCodeProvider will be created on the first request in the request and then reused in the web request, it will be permanently deleted when WebRequestLifestyleMiddleware calls lifetimeScope.Dispose() .

+3
source

All Articles