2009-07-19 11 views
44

में सिंगलटन प्रति कॉल संदर्भ (वेब ​​अनुरोध) कुछ दिन पहले मुझे एएसपी.Net थ्रेडिंग के साथ यह समस्या थी। मैं प्रति वेब अनुरोध एक सिंगलटन ऑब्जेक्ट चाहता था। मुझे वास्तव में काम की मेरी इकाई के लिए इसकी ज़रूरत है। मैं प्रति वेब अनुरोध के काम की इकाई को तुरंत चालू करना चाहता था ताकि पहचान मानचित्र अनुरोध के माध्यम से मान्य हो। इस तरह मैं अपने स्वयं के IUnitOfWork को अपने भंडार वर्गों में पारदर्शी रूप से इंजेक्ट करने के लिए एक आईओसी का उपयोग कर सकता हूं, और मैं क्वेरी के लिए एक ही उदाहरण का उपयोग कर सकता हूं और फिर मेरी इकाइयों को अपडेट कर सकता हूं।यूनिटी

चूंकि मैं एकता का उपयोग कर रहा हूं, मैंने गलती से PerThreadLifeTimeManager का उपयोग किया। मुझे जल्द ही एहसास हुआ कि एएसपी.Net थ्रेडिंग मॉडल उस चीज़ का समर्थन नहीं करता है जिसे मैं स्वीकार करना चाहता हूं। असल में यह एक थैडपूल का उपयोग करता है और थ्रेड को रीसायकल करता है, और इसका मतलब है कि मुझे प्रति यूनिटऑफवर्क मिल जाता है !! हालांकि, मैं जो चाहता था वह प्रति वेब अनुरोध के काम की एक इकाई थी।

कुछ गुगल ने मुझे this great post दिया। वही था जो मैं चाहता था; एकता भाग को छोड़कर जो आसानी से परेशान था।

public class PerCallContextLifeTimeManager : LifetimeManager 
{ 
    private const string Key = "SingletonPerCallContext"; 

    public override object GetValue() 
    { 
     return CallContext.GetData(Key); 
    } 

    public override void SetValue(object newValue) 
    { 
     CallContext.SetData(Key, newValue); 
    } 

    public override void RemoveValue() 
    { 
    } 
} 

और निश्चित रूप से मैं इस का उपयोग इस के समान एक कोड के साथ काम करने की मेरी यूनिट रजिस्टर करने के लिए:

unityContainer 
      .RegisterType<IUnitOfWork, MyDataContext>(
      new PerCallContextLifeTimeManager(), 
      new InjectionConstructor()); 

आशा है कि यह किसी की बचत होती है

यह एकता के लिए PerCallContextLifeTimeManager के लिए मेरे कार्यान्वयन है थोड़ा सा समय

+0

अच्छा समाधान। यदि मैं कर सकता हूं, तो मैं इसे "CallContextLifetimeManager" में नाम देने की अनुशंसा करता हूं क्योंकि वेब अनुरोध संभवतः संभावित अनुप्रयोगों में से एक हैं। –

+0

सच है, मैंने इसे प्रतिबिंबित करने के लिए पाठ और कोड अपडेट किया है। धन्यवाद। –

+0

+1 बहुत उपयोगी। – MrDustpan

उत्तर

25

साफ समाधान है, लेकिन LifetimeManager के प्रत्येक उदाहरण के बजाय एक निरंतर से एक अद्वितीय कुंजी का उपयोग करना चाहिए:

private string _key = string.Format("PerCallContextLifeTimeManager_{0}", Guid.NewGuid()); 

अन्यथा यदि आपके पास एक से अधिक PerCallContextLifeTimeManager के साथ पंजीकृत वस्तु, वे का उपयोग करने के लिए एक ही कुंजी साझा कर रहे हैं CallContext, और आपको अपनी अपेक्षित वस्तु वापस नहीं मिलेगी।

इसके अलावा RemoveValue को लागू वस्तुओं सुनिश्चित करने के लिए लायक साफ हो जाने:

public override void RemoveValue() 
{ 
    CallContext.FreeNamedDataSlot(_key); 
} 
+0

-1 कॉलकॉन्टेक्स्ट प्रत्येक अनुरोध पर पुनर्निर्मित किया गया है। कुंजी प्रति अनुरोध सिंगलटन के विभिन्न उदाहरणों के बीच अद्वितीय नहीं होना चाहिए। –

+8

+1 वास्तव में, यह छः की तरह ही होना चाहिए। यदि आप अद्वितीय कुंजी असाइन नहीं करते हैं तो सभी ऑब्जेक्ट्स एकल कुंजी के अंतर्गत पंजीकृत होते हैं और चीजें गड़बड़ हो जाती हैं। –

+0

-1 स्टीवन रॉबिन्स का जवाब देखें और प्रश्न पर मीका ज़ोलतु की टिप्पणी देखें: लोड के तहत 'कॉलकॉन्टेक्स्ट' को एक अनुरोध के लिए संरक्षित नहीं किया गया है। –

20

हालांकि यह ठीक बुला इस PerCallContextLifeTimeManager है, मैं बहुत यकीन है कि यह नहीं "सुरक्षित" है एक ASP.Net विचार किया जाना कर रहा हूँ प्रति अनुरोध लाइफटाइम प्रबंधक।

ASP.Net करता है अपने धागे की अदला-बदली तो केवल एक चीज CallContext के माध्यम से नए सूत्र को भर लिया वर्तमान HttpContext है - कुछ और आप CallContext में स्टोर चला जाएगा। इसका मतलब भारी भार के तहत उपरोक्त कोड में अनपेक्षित परिणाम हो सकते हैं - और मुझे लगता है कि यह ट्रैक करने के लिए एक असली दर्द होगा क्यों!

केवल "सुरक्षित" तरीका यह है HttpContext.Current.Items साथ है, या की तरह कुछ कर रही:

public class PerCallContextOrRequestLifeTimeManager : LifetimeManager 
{ 
    private string _key = string.Format("PerCallContextOrRequestLifeTimeManager_{0}", Guid.NewGuid()); 

    public override object GetValue() 
    { 
     if(HttpContext.Current != null) 
     return GetFromHttpContext(); 
     else 
     return GetFromCallContext(); 
    } 

    public override void SetValue(object newValue) 
    { 
     if(HttpContext.Current != null) 
     return SetInHttpContext(); 
     else 
     return SetInCallContext(); 
    } 

    public override void RemoveValue() 
    { 
    } 
} 

यह स्पष्ट रूप से प्रणाली पर निर्भरता लेने का मतलब है।पर

बहुत अधिक जानकारी :-(वेब इस यहां उपलब्ध है:

http://piers7.blogspot.com/2005/11/threadstatic-callcontext-and_02.html

3

अपने योगदान के लिए धन्यवाद,

लेकिन सवाल प्रस्तावित कार्यान्वयन दो कमियां, जिनमें से एक एक गंभीर बग है है के रूप में पहले से ही his answer और मीका Zoltu में स्टीवन रॉबिंस ने कहा in a comment

  1. कॉल संदर्भ नहीं है एक अनुरोध के लिए asp.net द्वारा संरक्षित होने की गारंटी नहीं है। लोड के तहत, यह किसी दूसरे पर स्विच कर सकता है, जिसके कारण ब्रेक को प्रस्तावित कार्यान्वयन होता है।
  2. यह अनुरोध अंत में निर्भरताओं को जारी करने में संभाल नहीं करता है।

वर्तमान में, यूनिटी.एमवीसी नुजेट पैकेज काम करने के लिए PerRequestLifetimeManager की आपूर्ति करता है। बूटस्ट्रैपिंग कोड में अपने संबंधित UnityPerRequestHttpModule को पंजीकृत करना न भूलें अन्यथा निर्भरता जारी करने को या तो संभाला नहीं जाएगा।

बूटस्ट्रैपिंग

DynamicModuleUtility.RegisterModule(typeof(UnityPerRequestHttpModule)); 

या web.config system.webServer/modules

<add name="UnityPerRequestHttpModule" type="Microsoft.Practices.Unity.Mvc.UnityPerRequestHttpModule, Microsoft.Practices.Unity.Mvc" preCondition="managedHandler" /> 

यह अपने वर्तमान कार्यान्वयन प्रकट होता है में उपयोग करते हुए भी वेब प्रपत्रों के लिए उपयुक्त है। और यह एमवीसी पर भी निर्भर नहीं है। दुर्भाग्यवश, इसकी असेंबली, इसमें कुछ अन्य वर्गों का कारण है।

सावधान रहें, यदि आप अपनी हल की गई निर्भरताओं का उपयोग करके कुछ कस्टम http मॉड्यूल का उपयोग करते हैं, तो वे मॉड्यूल EndRequest मॉड्यूल में पहले ही डिस्पोजेड हो सकते हैं। यह मॉड्यूल निष्पादन आदेश पर निर्भर करता है।

संबंधित मुद्दे