c# क्या सिंक्रनाइज़ेशन लॉक अपवाद को हर समय फेंकने के लिए एकता नहीं बनाई जा सकती है?




unity-container enterprise-library (7)

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

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

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

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

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


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


रोरी का समाधान बहुत अच्छा है - धन्यवाद। एक मुद्दा हल किया जो मुझे हर दिन परेशान करता है! मैंने रोरी के समाधान में कुछ मामूली बदलाव किए ताकि यह जो भी एक्सटेंशन पंजीकृत हो (मेरे मामले में मेरे पास एक 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);
            }
        }
    }

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

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


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

  • डीबग पर जाएं -> अपवाद ...
  • उन अपवादों को ढूंढें जो वास्तव में आपको सिंक्रनाइज़ेशन लॉकएक्सप्शन की तरह परेशान करते हैं

देखा।


Fixed

  1. थ्रेड सुरक्षा समस्या को संबोधित करना: http://unity.codeplex.com/discussions/328841

  2. सिस्टम पर डिबगिंग अनुभव में सुधार। थ्रेडिंग। सिंक्रनाइज़ेशन लॉक अपवाद: https://entlib.uservoice.com/forums/89245-general/suggestions/2377307-fix-the-system-threading-synchronizationlockexcep

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

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

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


मैं इस संक्षिप्त समाधान का उपयोग करता हूं:

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

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

सम्मान, कोएन


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

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

दूसरा परिदृश्य यह था कि प्रत्येक बार RegisterInstance कहलाता है, UnityDefaultBehaviorExtension पहले GetValue को कॉल किए बिना SetValue को कॉल करता है। सौभाग्य से, एकता पर्याप्त है कि पर्याप्त खूनी दिमागीपन के साथ, आप समस्या के आसपास काम कर सकते हैं।

इस तरह के एक नए व्यवहार विस्तार से शुरू करें:

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

फिर आपको डिफ़ॉल्ट व्यवहार को बदलने के लिए एक तरीका चाहिए। एकता के पास एक विशिष्ट एक्सटेंशन को हटाने का कोई तरीका नहीं है, इसलिए आपको सब कुछ हटाना होगा और अन्य एक्सटेंशन को फिर से रखना होगा:

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

ध्यान दें कि UnityClearBuildPlanStrategies ? 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();
    }
}

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

[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 { }






enterprise-library