24

मेरे पास एक वेब एप्लिकेशन है जहां .LifestylePerWebRequest() का उपयोग करके कई घटक पंजीकृत हैं, अब मैंने क्वार्ट्ज.NET, एक .NET नौकरी शेड्यूलिंग लाइब्रेरी लागू करने का निर्णय लिया है, जो अलग थ्रेड में निष्पादित करता है, और अनुरोध थ्रेड नहीं।कैसल। विन्डसर लाइफस्टाइल संदर्भ के आधार पर?

इस प्रकार, HttpContext.Currentnull उत्पन्न करता है। .LifestylePerWebRequest() का उपयोग करके मेरी सेवाओं, भंडारों, और IDbConnection को अभी तक इंस्टॉल किया गया था क्योंकि अनुरोध समाप्त होने पर इन्हें निपटाना आसान हो गया था।

अब मैं इन परिस्थितियों में दोनों परिदृश्यों में उपयोग करना चाहता हूं, वेब अनुरोधों के दौरान, मैं उन्हें अप्रभावित रहना चाहता हूं, और गैर-अनुरोध संदर्भों में मैं चाहता हूं कि वे एक अलग जीवन शैली का उपयोग करें, मुझे लगता है कि मैं खुद को निपटाने में संभाल सकता हूं, लेकिन वर्तमान संदर्भ के आधार पर घटकों के लिए जीवनशैली चुनने के लिए मुझे इसके बारे में कैसे जाना चाहिए?

वर्तमान में मैं सेवाओं (उदाहरण के लिए) रजिस्टर, इस तरह:

container.Register(
    AllTypes 
     .FromAssemblyContaining<EmailService>() 
     .Where(t => t.Name.EndsWith("Service")) 
     .WithService.Select(IoC.SelectByInterfaceConvention) 
     .LifestylePerWebRequest() 
); 

मैं समझ मैं विस्तार विधि के कुछ प्रकार का उपयोग करना चाहिए, लेकिन मैं सिर्फ यह नहीं दिख रहा है ..

+0

एएसपी.NET ऐप की पृष्ठभूमि में कोड चलाने के बारे में एक संबंधित प्रश्न है। प्रश्न सरल इंजेक्टर डी कंटेनर के बारे में है, लेकिन उत्तर अभी भी आपके लिए दिलचस्प हो सकता है: http://stackoverflow.com/a/11059491/264697। – Steven

उत्तर

21

आप castleprojectcontrib से Hybrid Lifestyle उपयोग करना चाहिए।

एक संकर जीवनशैली वह है जो वास्तव में दो अंतर्निहित जीवन शैली को जोड़ती है: मुख्य जीवनशैली और माध्यमिक जीवनशैली। हाइब्रिड लाइफस्टाइल पहले मुख्य जीवनशैली का उपयोग करने की कोशिश करता है; यदि यह किसी कारण से अनुपलब्ध है, तो यह माध्यमिक जीवनशैली का उपयोग करता है। यह आमतौर पर PerWebRequest के साथ मुख्य जीवनशैली के रूप में उपयोग किया जाता है: यदि HTTP संदर्भ उपलब्ध है, तो इसे घटक उदाहरण के दायरे के रूप में उपयोग किया जाता है; अन्यथा माध्यमिक जीवनशैली का उपयोग किया जाता है।

+7

NuGet के माध्यम से विंडसर 3 के लिए उपलब्ध: http://nuget.org/packages/Castle.Windsor.Lifestyles/0.2.0-alpha1 –

+0

मैंने साइनलआर के साथ कैसल विंडसर का उपयोग करते समय एक उपयोग केस दिखाते हुए एक पोस्ट लिखी है। यह वास्तव में आसान था! धन्यवाद। http: //www.leniel।नेट/2013/01/सिग्नल-ऑनडिस्कनेक्टेड-टास्क-एंड-निर्भरता-इंजेक्शन-साथ-castle.windsor-hybrid-lifestyle.html –

+0

गैर-संस्करण-विशिष्ट URL NuGet - https://www.nuget.org/ पैकेज/Castle.indsor.Lifestyles/ – PandaWood

1

मैं डॉन .LifestylePerWebRequest() में दृश्यों के पीछे क्या हो रहा है; लेकिन यह है कि मैं "संदर्भ प्रति अनुरोध" परिदृश्यों के लिए करता हूं:

सत्र के लिए HttpContext देखें और यदि मौजूद है .Items से संदर्भ खींचें। यदि यह अस्तित्व में नहीं है तो System.Threading.Thread.CurrentContext से अपना संदर्भ खींचें।

उम्मीद है कि इससे मदद मिलती है।

3

मुझे हाल ही में एक ही समस्या है - मैं एप्लिकेशन स्टार्टअप में अपने कंटेनर के आधार पर प्रारंभिक कोड चलाने में सक्षम होना चाहता था, जब HttpContext.Request अभी तक मौजूद नहीं है। मुझे ऐसा करने का कोई तरीका नहीं मिला, इसलिए मैंने जो भी चाहता था उसे करने की अनुमति देने के लिए मैंने PerWebRequestLifestyleModule के स्रोत को संशोधित किया। दुर्भाग्यवश विंडसर के पुन: संकलन के बिना इस बदलाव को संभव नहीं लग रहा था - मुझे आशा थी कि मैं इसे एक व्यापक तरीके से कर पाऊंगा ताकि मैं विंडसर के मुख्य वितरण का उपयोग जारी रख सकूं।

वैसे भी, यह काम करने के लिए, मैं PerWebRequestLifestyleModule की GetScope समारोह संशोधित इसलिए यदि यह एक HttpContext में नहीं चल रहा था (या अगर HttpContext.Request एक अपवाद फेंकता है, जैसे कि यह Application_Start में करता है) फिर इसे ढूंढें होगा कि इसके बजाय कंटेनर से एक दायरा शुरू किया।

using (var scope = container.BeginScope()) 
{ 
    // LifestylePerWebRequest components will now be scoped to this explicit scope instead 
    // _container.Resolve<...>() 

} 

, चिंता करने के बारे में स्पष्ट रूप से चीजों को निपटाने की आवश्यकता नहीं है क्योंकि वे निपटारा किया जाएगा जब स्कोप है: यह मैं निम्नलिखित कोड का उपयोग कर Application_Start में मेरी कंटेनर का उपयोग कर सकते हैं।

मैंने नीचे दिए गए मॉड्यूल के लिए पूरा कोड डाल दिया है। काम करने के लिए मुझे इस कक्षा के भीतर कुछ अन्य चीजों को घुमा देना पड़ा, लेकिन यह अनिवार्य रूप से वही है।

public class PerWebRequestLifestyleModule : IHttpModule 
{ 
    private const string key = "castle.per-web-request-lifestyle-cache"; 
    private static bool allowDefaultScopeOutOfHttpContext = true; 
    private static bool initialized; 

    public void Dispose() 
    { 
    } 

    public void Init(HttpApplication context) 
    { 
     initialized = true; 
     context.EndRequest += Application_EndRequest; 
    } 

    protected void Application_EndRequest(Object sender, EventArgs e) 
    { 
     var application = (HttpApplication)sender; 
     var scope = GetScope(application.Context, createIfNotPresent: false); 
     if (scope != null) 
     { 
      scope.Dispose(); 
     } 
    } 

    private static bool IsRequestAvailable() 
    { 
     if (HttpContext.Current == null) 
     { 
      return false; 
     } 

     try 
     { 
      if (HttpContext.Current.Request == null) 
      { 
       return false; 
      } 
      return true; 
     } 
     catch (HttpException) 
     { 
      return false; 
     } 
    } 

    internal static ILifetimeScope GetScope() 
    { 
     var context = HttpContext.Current; 
     if (initialized) 
     { 
      return GetScope(context, createIfNotPresent: true); 
     } 
     else if (allowDefaultScopeOutOfHttpContext && !IsRequestAvailable()) 
     { 
      // We're not running within a Http Request. If the option has been set to allow a normal scope to 
      // be used in this situation, we'll use that instead 
      ILifetimeScope scope = CallContextLifetimeScope.ObtainCurrentScope(); 
      if (scope == null) 
      { 
       throw new InvalidOperationException("Not running within a Http Request, and no Scope was manually created. Either run from within a request, or call container.BeginScope()"); 
      } 
      return scope; 
     } 
     else if (context == null) 
     { 
      throw new InvalidOperationException(
        "HttpContext.Current is null. PerWebRequestLifestyle can only be used in ASP.Net"); 
     } 
     else 
     { 
      EnsureInitialized(); 
      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 = HttpContext.Current; 
     if (context == null) 
     { 
      return null; 
     } 
     var scope = GetScope(context, createIfNotPresent: true); 
     if (scope != null) 
     { 
      context.Items.Remove(key); 
     } 
     return scope; 
    } 

    private static void EnsureInitialized() 
    { 
     if (initialized) 
     { 
      return; 
     } 
     var message = new StringBuilder(); 
     message.AppendLine("Looks like you forgot to register the http module " + typeof(PerWebRequestLifestyleModule).FullName); 
     message.AppendLine("To fix this add"); 
     message.AppendLine("<add name=\"PerRequestLifestyle\" type=\"Castle.MicroKernel.Lifestyle.PerWebRequestLifestyleModule, Castle.Windsor\" />"); 
     message.AppendLine("to the <httpModules> section on your web.config."); 
     if (HttpRuntime.UsingIntegratedPipeline) 
     { 
      message.AppendLine(
       "Windsor also detected you're running IIS in Integrated Pipeline mode. This means that you also need to add the module to the <modules> section under <system.webServer>."); 
     } 
     else 
     { 
      message.AppendLine(
       "If you plan running on IIS in Integrated Pipeline mode, you also need to add the module to the <modules> section under <system.webServer>."); 
     } 
#if !DOTNET35 
     message.AppendLine("Alternatively make sure you have " + PerWebRequestLifestyleModuleRegistration.MicrosoftWebInfrastructureDll + 
          " assembly in your GAC (it is installed by ASP.NET MVC3 or WebMatrix) and Windsor will be able to register the module automatically without having to add anything to the config file."); 
#endif 
     throw new ComponentResolutionException(message.ToString()); 
    } 

    private static ILifetimeScope GetScope(HttpContext context, bool createIfNotPresent) 
    { 
     var candidates = (ILifetimeScope)context.Items[key]; 
     if (candidates == null && createIfNotPresent) 
     { 
      candidates = new DefaultLifetimeScope(new ScopeCache()); 
      context.Items[key] = candidates; 
     } 
     return candidates; 
    } 
} 
+0

आप उत्तर को देखना चाहते हैं जो मैं कुछ मिनटों में पोस्ट करूँगा :) – bevacqua

2

ठीक है, मैंने यह करने के लिए एक बहुत साफ तरीका निकाला!

सबसे पहले हम IHandlerSelector के एक कार्यान्वयन की आवश्यकता होगी, इस मामले पर हमारी राय पर आधारित एक हैंडलर का चयन करें, या तटस्थ रहने (null, जिसका अर्थ है "कोई राय नहीं" वापस लौट कर) कर सकते हैं।

/// <summary> 
/// Emits an opinion about a component's lifestyle only if there are exactly two available handlers and one of them has a PerWebRequest lifestyle. 
/// </summary> 
public class LifestyleSelector : IHandlerSelector 
{ 
    public bool HasOpinionAbout(string key, Type service) 
    { 
     return service != typeof(object); // for some reason, Castle passes typeof(object) if the service type is null. 
    } 

    public IHandler SelectHandler(string key, Type service, IHandler[] handlers) 
    { 
     if (handlers.Length == 2 && handlers.Any(x => x.ComponentModel.LifestyleType == LifestyleType.PerWebRequest)) 
     { 
      if (HttpContext.Current == null) 
      { 
       return handlers.Single(x => x.ComponentModel.LifestyleType != LifestyleType.PerWebRequest); 
      } 
      else 
      { 
       return handlers.Single(x => x.ComponentModel.LifestyleType == LifestyleType.PerWebRequest); 
      } 
     } 
     return null; // we don't have an opinion in this case. 
    } 
} 

मैंने इसे बनाया ताकि राय उद्देश्य पर बहुत सीमित हो। मैं केवल एक राय रखूंगा यदि वास्तव में दो हैंडलर हैं और उनमें से एक PerWebRequest जीवनशैली है; जिसका अर्थ है कि दूसरा शायद गैर-एचटीपी कॉन्टेक्स्ट विकल्प है।

हमें इस चयनकर्ता को कैसल के साथ पंजीकृत करने की आवश्यकता है। मैं ऐसा करने से पहले मैं किसी भी अन्य घटकों को पंजीकृत करने शुरू:

container.Kernel.AddHandlerSelector(new LifestyleSelector()); 

अन्त काश मैं मैं अपने पंजीकरण कैसे कॉपी इस से बचने के लिए कर सकता है के बारे में कोई सुराग नहीं था:

container.Register(
    AllTypes 
     .FromAssemblyContaining<EmailService>() 
     .Where(t => t.Name.EndsWith("Service")) 
     .WithService.Select(IoC.SelectByInterfaceConvention) 
     .LifestylePerWebRequest() 
); 

container.Register(
    AllTypes 
     .FromAssemblyContaining<EmailService>() 
     .Where(t => t.Name.EndsWith("Service")) 
     .WithService.Select(IoC.SelectByInterfaceConvention) 
     .LifestylePerThread() 
); 

आप पता लगा सकते हैं एक पंजीकरण क्लोन करने, जीवनशैली बदलने और उन दोनों को पंजीकृत करने के लिए (container.Register या IRegistration.Register का उपयोग करके), कृपया इसे यहां एक उत्तर के रूप में पोस्ट करें! :)

अद्यतन: परीक्षण में, मैं विशिष्ट समान पंजीकरण नाम के लिए की जरूरत है, मैं इस तरह वैसा ही किया:

.NamedRandomly() 


    public static ComponentRegistration<T> NamedRandomly<T>(this ComponentRegistration<T> registration) where T : class 
    { 
     string name = registration.Implementation.FullName; 
     string random = "{0}{{{1}}}".FormatWith(name, Guid.NewGuid()); 
     return registration.Named(random); 
    } 

    public static BasedOnDescriptor NamedRandomly(this BasedOnDescriptor registration) 
    { 
     return registration.Configure(x => x.NamedRandomly()); 
    } 
+0

दिलचस्प। मैं शायद अपने दृष्टिकोण के साथ रहूंगा क्योंकि यह सबकुछ दो बार पंजीकृत करने के लिए बचाता है, और उम्मीद है कि मुझे इसे एकीकृत करने का एक आसान तरीका मिल सकता है। यदि आप Application_Start में कुछ भी करने के लिए अपने दृष्टिकोण का उपयोग करना चाहते हैं, तो आपको अपने हैंडलर चयनकर्ता को संशोधित करने की आवश्यकता होगी ताकि वह उस मामले को भी पकड़ ले जहां HttpRequest.Current मौजूद है लेकिन HttpRequest.Current.Request नहीं है। – Richard

6

समान घटकों का उपयोग न करें। असल में, ज्यादातर परिदृश्यों में मैंने देखा है कि "पृष्ठभूमि प्रसंस्करण" शुरू करने के लिए वेब प्रक्रिया में भी समझ में नहीं आता है।

टिप्पणियों के आधार पर विस्तारित।

वेब पाइपलाइन में शोहोर्निंग पृष्ठभूमि प्रसंस्करण ईसी 2 उदाहरण पर कुछ $ बचाने के लिए आपके आर्किटेक्चर से समझौता कर रहा है। मैं दृढ़ता से इस बारे में सोचने का सुझाव दूंगा, लेकिन मैं digress।

मेरे बयान अभी भी खड़े हैं, भले ही आप दोनों प्रक्रियाओं को वेब प्रक्रिया में डाल रहे हों, वे दो अलग-अलग संदर्भों में दो अलग-अलग घटक हैं और इन्हें इस तरह माना जाना चाहिए।

+0

देखभाल करने के लिए देखभाल? – bevacqua

+0

किस भाग पर ?? –

+0

"विभिन्न संदर्भों में घटकों को पुन: उपयोग करने पर भी समझ में नहीं आता है"; मैं वेब सेवाओं के भीतर अपनी सेवाएं चलाता हूं क्योंकि सर्वर जो मैं तैनात कर रहा हूं वह मुझे विंडोज सेवाओं की मेजबानी करने की अनुमति नहीं देता है। – bevacqua