2010-05-20 23 views
64

यूनिटी निर्भरता इंजेक्शन कंटेनर में व्यापक रूप से ज्ञात समस्या प्रतीत होती है जहां सिंक्रनाइज़ किया गया लाइफटाइम मैनेजर अक्सर मॉनीटर का कारण बनता है। सिंक्रनाइज़ेशन लॉकएक्सप्शन को फेंकने के लिए विधि विधि जिसे पकड़ा और अनदेखा किया जाता है। यह मेरे लिए एक समस्या है क्योंकि मैं किसी भी फेंकने वाले अपवाद को तोड़ने के लिए विजुअल स्टूडियो सेट के साथ डीबग करना पसंद करता हूं, इसलिए हर बार जब मेरा एप्लिकेशन शुरू होता है तो मैं किसी भी कारण से इस अपवाद पर कई बार तोड़ रहा हूं।क्या यूनिटी सिंक्रनाइज़ेशन लॉक अपवाद को हर समय फेंकने के लिए नहीं बनाया जा सकता है?

मैं इस अपवाद को फेंकने से कैसे रोक सकता हूं?

जहां भी इस मुद्दे का वेब पर कहीं और उल्लेख किया गया है, सलाह में आमतौर पर इसे अनदेखा करने के लिए डीबगर सेटिंग्स को बदलना शामिल होता है। यह चिकित्सक के पास जाने और कहने के समान है, "चिकित्सक, चिकित्सक, जब मैं इसे उठाता हूं तो मेरी भुजा दर्द होती है," कहा, "ठीक है, इसे उठाना बंद करो।" मैं ऐसे समाधान की तलाश में हूं जो अपवाद को पहले स्थान पर फेंक दिया जाए।

अप्वाल्लू विधि में अपवाद होता है क्योंकि यह धारणा बनाता है कि GetValue को पहले कॉल किया जाएगा, जहां मॉनिटर.इंटर कहा जाता है। हालांकि, लाइफटाइमस्ट्रेटी और यूनिटी डीफॉल्टबैवियर एक्सटेंशन एक्सटेंशन दोनों नियमित रूप से GetValue को GetValue को कॉल किए बिना कॉल करते हैं।

मुझे स्रोत कोड को बदलने और यूनिटी के अपने संस्करण को बनाए रखने की आवश्यकता नहीं है, इसलिए मैं एक समाधान की उम्मीद कर रहा हूं जहां मैं कंटेनर को एक्सटेंशन, नीतियों या रणनीतियों का कुछ संयोजन जोड़ सकता हूं जो सुनिश्चित करेगा कि, यदि आजीवन प्रबंधक एक सिंक्रनाइज़ लाइफटाइम मैनेजर है, तो GetValue को किसी और चीज़ से पहले हमेशा बुलाया जाता है।

उत्तर

38

मुझे यकीन है कि कोड सिंक्रनाइज़ किए गए लाइफटाइम मैनेजर या कंटेनर कंट्रोलर लाइफटाइम मैनेजर जैसे वंशज हैं, लेकिन विशेष रूप से दो परिदृश्य थे जो मुझे समस्याएं पैदा कर रहे थे।

पहली बार मेरी अपनी गलती थी - मैं कंटेनर के संदर्भ की आपूर्ति के लिए कन्स्ट्रक्टर इंजेक्शन का उपयोग कर रहा था, और उस कन्स्ट्रक्टर में मैं भविष्य के उपयोग के लिए कंटेनर में कक्षा का नया उदाहरण भी जोड़ रहा था। इस पिछड़े दृष्टिकोण से क्षणिक प्रबंधक को कंटेनर से नियंत्रित करने के प्रभाव को नियंत्रित किया गया था ताकि ऑब्जेक्ट यूनिटी जिसे गेटवैल्यू कहा जाता है, वही ऑब्जेक्ट नहीं था जिसे इसे सेटवैल्यू कहा जाता था। सीखने वाला सबक बिल्ड-अप के दौरान कुछ भी नहीं करता है जो किसी ऑब्जेक्ट के आजीवन प्रबंधक को बदल सकता है।

दूसरा परिदृश्य यह था कि प्रत्येक बार रजिस्टरइंस्टेंस कहा जाता है, यूनिटीडिफॉल्टबैवियर एक्सटेंशन ने GetValue को पहले GetValue को कॉल किए बिना कॉल किया है। सौभाग्य से, एकता पर्याप्त है कि पर्याप्त खूनी दिमागीपन के साथ, आप समस्या के आसपास काम कर सकते हैं।

इस तरह एक नया व्यवहार विस्तार के साथ प्रारंभ:

/// <summary> 
/// Replaces <see cref="UnityDefaultBehaviorExtension"/> to eliminate 
/// <see cref="SynchronizationLockException"/> exceptions that would otherwise occur 
/// when using <c>RegisterInstance</c>. 
/// </summary> 
public class UnitySafeBehaviorExtension : UnityDefaultBehaviorExtension 
{ 
    /// <summary> 
    /// Adds this extension's behavior to the container. 
    /// </summary> 
    protected override void Initialize() 
    { 
     Context.RegisteringInstance += PreRegisteringInstance; 

     base.Initialize(); 
    } 

    /// <summary> 
    /// Handles the <see cref="ExtensionContext.RegisteringInstance"/> event by 
    /// ensuring that, if the lifetime manager is a 
    /// <see cref="SynchronizedLifetimeManager"/> that its 
    /// <see cref="SynchronizedLifetimeManager.GetValue"/> method has been called. 
    /// </summary> 
    /// <param name="sender">The object responsible for raising the event.</param> 
    /// <param name="e">A <see cref="RegisterInstanceEventArgs"/> containing the 
    /// event's data.</param> 
    private void PreRegisteringInstance(object sender, RegisterInstanceEventArgs e) 
    { 
     if (e.LifetimeManager is SynchronizedLifetimeManager) 
     { 
      e.LifetimeManager.GetValue(); 
     } 
    } 
} 

तो फिर तुम एक तरह से डिफ़ॉल्ट व्यवहार को बदलने के लिए की जरूरत है। एकता एक विशिष्ट एक्सटेंशन को निकालने के लिए एक विधि नहीं है, तो आप सब कुछ को हटा दें और दोबारा अन्य एक्सटेंशन डाल करने के लिए है: कि UnityClearBuildPlanStrategies

public static IUnityContainer InstallCoreExtensions(this IUnityContainer container) 
{ 
    container.RemoveAllExtensions(); 
    container.AddExtension(new UnityClearBuildPlanStrategies()); 
    container.AddExtension(new UnitySafeBehaviorExtension()); 

#pragma warning disable 612,618 // Marked as obsolete, but Unity still uses it internally. 
    container.AddExtension(new InjectedMembers()); 
#pragma warning restore 612,618 

    container.AddExtension(new UnityDefaultStrategiesExtension()); 

    return container; 
} 

सूचना? RemoveAllExtensions नीतियों और एक को छोड़कर रणनीतियों के कंटेनर के आंतरिक सभी सूचियों को बाहर साफ करता है, तो मैं एक और एक्सटेंशन का उपयोग करने डुप्लिकेट डालने जब मैं डिफ़ॉल्ट एक्सटेंशन बहाल से बचने के लिए किया था:

/// <summary> 
/// Implements a <see cref="UnityContainerExtension"/> that clears the list of 
/// build plan strategies held by the container. 
/// </summary> 
public class UnityClearBuildPlanStrategies : UnityContainerExtension 
{ 
    protected override void Initialize() 
    { 
     Context.BuildPlanStrategies.Clear(); 
    } 
} 

अब आप सुरक्षित रूप डर के बिना RegisterInstance उपयोग कर सकते हैं पागलपन के कगार पर संचालित होने के लिए। बस सुनिश्चित करने के लिए, यहां कुछ परीक्षण हैं:

[TestClass] 
public class UnitySafeBehaviorExtensionTests : ITest 
{ 
    private IUnityContainer Container; 
    private List<Exception> FirstChanceExceptions; 

    [TestInitialize] 
    public void TestInitialize() 
    { 
     Container = new UnityContainer(); 
     FirstChanceExceptions = new List<Exception>(); 
     AppDomain.CurrentDomain.FirstChanceException += FirstChanceExceptionRaised; 
    } 

    [TestCleanup] 
    public void TestCleanup() 
    { 
     AppDomain.CurrentDomain.FirstChanceException -= FirstChanceExceptionRaised; 
    } 

    private void FirstChanceExceptionRaised(object sender, FirstChanceExceptionEventArgs e) 
    { 
     FirstChanceExceptions.Add(e.Exception); 
    } 

    /// <summary> 
    /// Tests that the default behavior of <c>UnityContainer</c> leads to a <c>SynchronizationLockException</c> 
    /// being throw on <c>RegisterInstance</c>. 
    /// </summary> 
    [TestMethod] 
    public void UnityDefaultBehaviorRaisesExceptionOnRegisterInstance() 
    { 
     Container.RegisterInstance<ITest>(this); 

     Assert.AreEqual(1, FirstChanceExceptions.Count); 
     Assert.IsInstanceOfType(FirstChanceExceptions[0], typeof(SynchronizationLockException)); 
    } 

    /// <summary> 
    /// Tests that <c>UnitySafeBehaviorExtension</c> protects against <c>SynchronizationLockException</c>s being 
    /// thrown during calls to <c>RegisterInstance</c>. 
    /// </summary> 
    [TestMethod] 
    public void SafeBehaviorPreventsExceptionOnRegisterInstance() 
    { 
     Container.RemoveAllExtensions(); 
     Container.AddExtension(new UnitySafeBehaviorExtension()); 
     Container.AddExtension(new InjectedMembers()); 
     Container.AddExtension(new UnityDefaultStrategiesExtension()); 

     Container.RegisterInstance<ITest>(this); 

     Assert.AreEqual(0, FirstChanceExceptions.Count); 
    } 
} 

public interface ITest { } 
+0

हम एंटरप्राइज़ लाइब्रेरी लॉगर का उपयोग कर रहे हैं लेकिन एकता नहीं है और हमारे पास एक ही समस्या है क्योंकि लॉगर आंतरिक रूप से एकता का उपयोग करता है। क्या मैं एकता के बिना उस फिक्स को लागू कर सकता हूं? –

+0

@DmitryGusarov मैंने इसे आजमाया नहीं है, लेकिन यदि आप 'यूनिटीकॉन्टेनर' उदाहरण का संदर्भ प्राप्त कर सकते हैं तो EntLib आंतरिक रूप से उपयोग करता है, तो आप उस पर 'InstallCoreExtensions' विधि का उपयोग करने में सक्षम होना चाहिए। आपको यह जांचना चाहिए कि EntLib अपने स्वयं के किसी भी एक्सटेंशन को नहीं जोड़ता है - यदि ऐसा होता है, तो आपको 'इंस्टॉलकोर एक्सटेंशन' के बाद उन्हें वापस जोड़ना होगा। –

+4

इस समाधान का उपयोग यूनिटी के नवीनतम संस्करण के साथ नहीं करें, यह अनंत लूप बनाता है। – Softlion

-8

यह आपकी मदद कर सकता है:

  • जाओ डीबग करने के लिए -> अपवाद ...
  • अपवाद का पता लगाएं कि वास्तव में आप परेशान SynchronizationLockException

देखा की तरह।

+3

यही मेरा मतलब है, "सलाह में आम तौर पर इसे अनदेखा करने के लिए डीबगर सेटिंग्स को बदलना शामिल होता है।" मैं किसी सिंक्रनाइज़ेशन लॉक अपवादों को स्क्वैश नहीं करना चाहता हूं कि मेरे अपने कोड में कीड़े हो सकती हैं। –

10

दुर्भाग्य से आपके प्रश्न का उत्तर दुर्भाग्य से नहीं है। मैंने यहां माइक्रोसॉफ्ट पैटर्न & प्रैक्टिस ग्रुप (मैं हाल ही में देव लीड था) में देव टीम के साथ इसका पालन किया था और हमारे पास यह एंटीलिब 5.0 के लिए विचार करने के लिए एक बग के रूप में था। हमने कुछ जांच की और निष्कर्ष निकाला कि यह हमारे कोड और डीबगर के बीच कुछ अप्रत्याशित बातचीत के कारण हुआ था। हमने एक फिक्स पर विचार किया लेकिन यह मौजूदा कोड की तुलना में अधिक जटिल साबित हुआ। अंत में यह अन्य चीजों के नीचे प्राथमिकता प्राप्त हुई और 5 के लिए बार नहीं बनाया।

क्षमा करें मेरे पास आपके लिए बेहतर जवाब नहीं है। यदि यह कोई सांत्वना है तो मुझे यह भी परेशान लगता है।

+1

घोड़े के मुंह से जवाब के लिए धन्यवाद! खुशी से, मेरे लिए समस्या के आसपास काम करने में सक्षम होने के लिए एकता पर्याप्त है - मेरा [उत्तर] देखें (http://stackoverflow.com/questions/2873767/can-unity-be-made-to-not-throw-synchronizationlockexception -सभी-समय/3153585 # 3153585)। –

+0

एडी ने EntLib5.0 विकास के समय EntLib टीम के निर्णय के पूरी तरह से संक्षेप में सारांशित किया। यदि यह कई लोगों के लिए एक बड़ा परेशान कारक है, तो कृपया इसे हमारे uservoice साइट पर सुझाव दें और इसके लिए वोट दें। एकता और EntLib के vNext की योजना बनाते समय हम इसे विचार करेंगे। –

4

रोरी का समाधान बहुत अच्छा है - धन्यवाद। एक मुद्दा हल किया जो मुझे हर दिन परेशान करता है! मैं रोरी का समाधान करने के लिए कुछ मामूली सुधार किया है ताकि यह संभालती है जो कुछ एक्सटेंशन पंजीकृत हैं (मेरे मामले में मैं एक WPF प्रिज्म/कम्पोजिट एक्सटेंशन था) ..

public static void ReplaceBehaviourExtensionsWithSafeExtension(IUnityContainer container) 
    { 
     var extensionsField = container.GetType().GetField("extensions", BindingFlags.Instance | BindingFlags.NonPublic); 
     var extensionsList = (List<UnityContainerExtension>)extensionsField.GetValue(container); 
     var existingExtensions = extensionsList.ToArray(); 
     container.RemoveAllExtensions(); 
     container.AddExtension(new UnitySafeBehaviorExtension()); 
     foreach (var extension in existingExtensions) 
     { 
      if (!(extension is UnityDefaultBehaviorExtension)) 
      { 
       container.AddExtension(extension); 
      } 
     } 
    } 
+0

निर्माण योजना रणनीतियों स्पष्ट करने के लिए याद रखें - 'container.AddExtension (नई UnityClearBuildPlanStrategies());' –

1

जुबिन Appoo के जवाब में एक गलती करने के लिए सावधान: नहीं है UnityClearBuildPlanStrategies अपने कोड में अनुपलब्ध है।

सही कोड स्निपेट है:

FieldInfo extensionsField = container.GetType().GetField("extensions", BindingFlags.Instance | BindingFlags.NonPublic); 
List<UnityContainerExtension> extensionsList = (List<UnityContainerExtension>)extensionsField.GetValue(container); 
UnityContainerExtension[] existingExtensions = extensionsList.ToArray(); 
container.RemoveAllExtensions(); 
container.AddExtension(new UnityClearBuildPlanStrategiesExtension()); 
container.AddExtension(new UnitySafeBehaviorExtension()); 

foreach (UnityContainerExtension extension in existingExtensions) 
{ 
    if (!(extension is UnityDefaultBehaviorExtension)) 
    { 
     container.AddExtension(extension); 
    } 
} 
+1

यह कोड स्निपेट जुबिन के जवाब के समान है और UnityClearBuildPlanStrategies() –

+0

आप ठीक कह रहे हैं, निश्चित –

7

मैं इस छोटे से समाधान का उपयोग करें:

/// <summary> 
/// KVV 20110502 
/// Fix for bug in Unity throwing a synchronizedlockexception at each register 
/// </summary> 
class LifeTimeManager : ContainerControlledLifetimeManager 
{ 
    protected override void SynchronizedSetValue(object newValue) 
    { 
     base.SynchronizedGetValue(); 
     base.SynchronizedSetValue(newValue); 
    } 
} 

और इस तरह इसका इस्तेमाल:

private UnityContainer _container; 
... 
_container.RegisterInstance(instance, new LifeTimeManager()); 

समस्या यह है कि आधार वर्ग ContainerControlledLifetimeManager की एक सिंक्रनाइज़ किए गए SetValue को मॉनीटर करने की अपेक्षा करता है। आधार के माध्यम से प्रवेश करें (GetValue, हो कंटेंटर कंट्रोलर लाइफटाइम मैनेजर क्लास ऐसा करने में असफल रहता है (जाहिर है इसके डेवलपर्स को 'अपवाद पर ब्रेक' सक्षम नहीं है?)।

संबंध है, कोएन

+0

यह एक है करने के लिए कॉल शामिल नहीं है सिल्वरलाइट के लिए अच्छा समाधान! – Kit

+0

मैं इस फिक्स को एप्लिकेशन में कैसे पेश कर सकता हूं? मुझे यह नहीं मिला ... क्या मुझे कहीं लाइफटाइम प्रबंधक पंजीकृत करना चाहिए? हम एकता का उपयोग नहीं कर रहे हैं ... बस यह लॉगर ... –

12

एकता (2.1.505.2) की नवीनतम रिलीज में Fixed। NuGet के माध्यम से इसे प्राप्त करें।

0

Unity 2.1 - August 2012 update fix the bug

  1. एक धागा सुरक्षा मुद्दे को संबोधित करते: http://unity.codeplex.com/discussions/328841

  2. System.Threading पर डिबगिंग अनुभव को बेहतर बनाना।SynchronizationLockException: https://entlib.uservoice.com/forums/89245-general/suggestions/2377307-fix-the-system-threading-synchronizationlockexcep

  3. बेहतर त्रुटि संदेश के माध्यम से डीबगिंग अनुभव में सुधार एक प्रकार लोड नहीं कर सकते हैं जब जा: http://unity.codeplex.com/workitem/9223

  4. एक वर्ग जो ऐसा नहीं करता एक मौजूदा उदाहरण पर एक निर्माण() के प्रदर्शन के एक परिदृश्य का समर्थन http://unity.codeplex.com/workitem/9460

उपयोगकर्ताओं के लिए संभव के रूप में सरल अद्यतन अनुभव बनाने के लिए और nee से बचने के लिए: एक सार्वजनिक निर्माता है डी असेंबली बाध्यकारी रीडायरेक्ट के लिए, हमने केवल असेंबली फ़ाइल संस्करण को बढ़ाने का चयन किया, न कि .NET असेंबली संस्करण।

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