2015-07-14 8 views
5

के साथ कैसल विंडसर की PerWebRequest लाइफस्टाइल का उपयोग कैसे करें IWIN का उपयोग करने के लिए मैं मौजूदा एएसपी .Net वेब एपीआई 2 प्रोजेक्ट को परिवर्तित कर रहा हूं। प्रोजेक्ट कैसल विंडसर को निर्भरता इंजेक्शन ढांचे के रूप में उपयोग करता है जिसमें परवेब्रावेस्ट लाइफस्टाइल का उपयोग करने के लिए सेट निर्भरताओं में से एक है।ओडिन

जब मैं सर्वर से अनुरोध करता हूं तो मुझे Castle.MicroKernel.ComponentResolutionException अपवाद मिलता है। अपवाद जोड़ने का सुझाव कॉन्फ़िग फ़ाइल में system.web/httpModules और system.WebServer/modules वर्गों के लिए निम्न:

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

यह त्रुटि का समाधान नहीं होता।

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

दुर्भाग्य से यह काम नहीं किया है:

SimpleInjector के Owin एकीकरण द्वारा प्रदान उदाहरण से प्रेरणा ले रहा है, मैं Owin स्टार्टअप कक्षा में एक गुंजाइश स्थापित करने के लिए (और साथ ही निर्भरता की जीवन शैली को अद्यतन) का उपयोग करने का प्रयास किया या तो।

मैं कैसल विंडसर की PerWebRequest जीवनशैली का उपयोग कैसे कर सकता हूं या इसे ओविन में अनुकरण कर सकता हूं?

उत्तर

6

कैसल विंडसर दस्तावेज़ के अनुसार आप अपना खुद का कस्टम दायरा लागू कर सकते हैं। आपको Castle.MicroKernel.Lifestyle.Scoped.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:

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

वर्ग OwinWebRequestScopeAccessor Castle.Windsor के IScopeAccessor लागू करता है:

इसके बाद आप जब अपने घटक के पंजीकरण के अपने दायरे एक्सेसर निर्दिष्ट और से PerWebRequestLifestyleOwinMiddleware का निपटान करें।

कक्षा PerWebRequestLifestyleOwinMiddleware कैसल विंडसर के एएसपी.नेट IHttpModule PerWebRequestLifestyleModule का ओविन काउंटर हिस्सा है।

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 एक प्रति वेब अनुरोध के आधार पर कैसल विंडसर ILifetimeScope के भंडारण के लिए HttpContext.Current का इस्तेमाल करता है:

यह PerWebRequestLifestyleOwinMiddleware वर्ग है। PerWebRequestLifestyleOwinMiddleware कक्षा OwinRequestScopeContext.Current का उपयोग करता है। यह Yoshifumi Kawai के विचार पर आधारित है।

नीचे OwinRequestScopeContext का कार्यान्वयन योशिफुमी कावाई के मूल OwinRequestScopeContext का हल्का कार्यान्वयन है।


नोट:

PM> Install-Package OwinRequestScopeContext


लाइटवेट: यदि आप इस हल्के कार्यान्वयन आप NuGet पैकेज प्रबंधक कंसोल में यह आदेश चलाकर Yoshifumi Kawai उत्तम मूल कार्यान्वयन का उपयोग कर सकते नहीं करना चाहते, तो 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(); 
      } 
     } 
    } 
} 

जब आपके पास सभी टुकड़े होते हैं तो आप चीजों को बांध सकते हैं। अपने ओविन स्टार्टअप क्लास में ऊपर परिभाषित ओविन मध्य वेयर पंजीकृत करने के लिए appBuilder.UsePerWebRequestLifestyleOwinMiddleware(); विस्तार विधि को कॉल करें। 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 प्रति-वेब अनुरोध गुंजाइश का उपयोग कर सकते हैं:

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

+0

दिलचस्प - मैं 'System.Runtime.Remoting.Messaging.CallContext' में यह स्टोर 'OwinRequestScopeContext' देखता हूं। मेरा पहला विचार ओविन पर्यावरण शब्दकोश में इसे स्टोर करना होगा। ('OwinContext.Environment')। –

+0

हालांकि आगे की परीक्षा में मुझे लगता है कि मैं एक कारण देख सकता हूं: 'System.Runtime.Remoting.Messaging.CallContext' वैश्विक रूप से सुलभ स्थिर सदस्य हैं, लेकिन' OwinContext' को स्थिर रूप से हासिल करने का कोई सुरक्षित तरीका नहीं है। –

2

मैंने Johan Boonstra's answer को लागू करने का प्रयास किया, लेकिन पाया कि यह एएसपी.नेट एमवीसी नियंत्रक विधियों के बाद एक बार काम नहीं कर रहा था।

यहाँ एक सरल समाधान है:

सबसे पहले, कुछ Owin मिडलवेयर कि पाइप लाइन के शुरू में बैठता है और बनाता है एक 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(); 
     } 
    } 
} 

अपने स्टार्टअप विन्यास में पाइप लाइन की शुरुआत में यह डालें बनाने :

public void Configure(IAppBuilder appBuilder) 
{ 
    appBuilder.Use<WebRequestLifestyleMiddleware>(); 

    // 
    // Further configuration 
    // 
} 

अब आप एक वर्ग है कि IScopeAccessor लागू करता है और गुंजाइश है किको हासिल करेगा बनानेवातावरण में धकेल दिया: जब घटक जीवन काल दर्ज की

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 जो मैं एक अनुरोध भर का पुन: उपयोग करना चाहते हैं कहा जाता है:

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

इस मामले में, AccessCodeProvider पहली बार यह एक अनुरोध के भीतर के लिए कहा है बनाया जाएगा, और फिर वेब भर में पुन: उपयोग किया अनुरोध, अंततः WebRequestLifestyleMiddleware का निपटान lifetimeScope.Dispose() का आह्वान करता है।