मुझे यकीन है कि कोड सिंक्रनाइज़ किए गए लाइफटाइम मैनेजर या कंटेनर कंट्रोलर लाइफटाइम मैनेजर जैसे वंशज हैं, लेकिन विशेष रूप से दो परिदृश्य थे जो मुझे समस्याएं पैदा कर रहे थे।
पहली बार मेरी अपनी गलती थी - मैं कंटेनर के संदर्भ की आपूर्ति के लिए कन्स्ट्रक्टर इंजेक्शन का उपयोग कर रहा था, और उस कन्स्ट्रक्टर में मैं भविष्य के उपयोग के लिए कंटेनर में कक्षा का नया उदाहरण भी जोड़ रहा था। इस पिछड़े दृष्टिकोण से क्षणिक प्रबंधक को कंटेनर से नियंत्रित करने के प्रभाव को नियंत्रित किया गया था ताकि ऑब्जेक्ट यूनिटी जिसे गेटवैल्यू कहा जाता है, वही ऑब्जेक्ट नहीं था जिसे इसे सेटवैल्यू कहा जाता था। सीखने वाला सबक बिल्ड-अप के दौरान कुछ भी नहीं करता है जो किसी ऑब्जेक्ट के आजीवन प्रबंधक को बदल सकता है।
दूसरा परिदृश्य यह था कि प्रत्येक बार रजिस्टरइंस्टेंस कहा जाता है, यूनिटीडिफॉल्टबैवियर एक्सटेंशन ने 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 { }
हम एंटरप्राइज़ लाइब्रेरी लॉगर का उपयोग कर रहे हैं लेकिन एकता नहीं है और हमारे पास एक ही समस्या है क्योंकि लॉगर आंतरिक रूप से एकता का उपयोग करता है। क्या मैं एकता के बिना उस फिक्स को लागू कर सकता हूं? –
@DmitryGusarov मैंने इसे आजमाया नहीं है, लेकिन यदि आप 'यूनिटीकॉन्टेनर' उदाहरण का संदर्भ प्राप्त कर सकते हैं तो EntLib आंतरिक रूप से उपयोग करता है, तो आप उस पर 'InstallCoreExtensions' विधि का उपयोग करने में सक्षम होना चाहिए। आपको यह जांचना चाहिए कि EntLib अपने स्वयं के किसी भी एक्सटेंशन को नहीं जोड़ता है - यदि ऐसा होता है, तो आपको 'इंस्टॉलकोर एक्सटेंशन' के बाद उन्हें वापस जोड़ना होगा। –
इस समाधान का उपयोग यूनिटी के नवीनतम संस्करण के साथ नहीं करें, यह अनंत लूप बनाता है। – Softlion