it-swarm.dev

Unity, SynchronizationLockException'ı her zaman atmamak için yapılabilir mi?

Birlik bağımlılığı enjeksiyon kabı, SynchronizedLifetimeManager uygulamasının sıklıkla Monitor.Exit yönteminin, daha sonra yakalanan ve yok sayılan bir SynchronizationLockException'ı atmasına neden olacağı yaygın olarak bilinen bir soruna sahiptir. Bu benim için bir sorun çünkü herhangi bir özel durumun önüne geçmek için ayarlanmış Visual Studio ile hata ayıklamayı seviyorum, bu yüzden başvurum her başladığında sebepsiz yere birden fazla defa bu istisnayı çiğniyorum.

Bu istisnanın atılmasını nasıl önleyebilirim?

Bu sorunların web üzerinde başka bir yerde belirtildiği yerlerde, tavsiye genellikle hata ayıklayıcı ayarlarının yok sayılması için değiştirilmesini içerir. Bu, doktora gitmeye ve "Doktor, Doktor, kolum ağrıyorken ağrıyor" demeye benziyor, "Şey, yükseltmeyi bırak." İlk başta atılan istisnayı durduran bir çözüm arıyorum.

Bunun istisnası, SetValue yönteminde gerçekleşir, çünkü GetValue'nun ilk olarak ve Monitor.Enter adının çağrıldığı varsayılır. Ancak, LifetimeStrategy ve UnityDefaultBehaviorExtension sınıflarının her ikisi de düzenli olarak GetValue çağırmadan SetValue öğesini çağırır.

Kaynak kodunu değiştirmek ve kendi Unity versiyonumu sürdürmek zorunda kalmamayı tercih ediyorum, bu nedenle, aşağıdakileri sağlayacak kabın içine bir miktar genişletme, politika veya strateji kombinasyonu ekleyebileceğim bir çözüm umuyorum. ömür boyu yöneticisi bir SynchronizedLifetimeManager, GetValue her zaman her şeyden önce çağrılır.

64
Rory MacLeod

Kodun SynchronizedLifetimeManager veya ContainerControlledLifetimeManager gibi bir soyundan çağırabileceğinin pek çok yolu olduğundan eminim, ancak özellikle bana sorun yapan iki senaryo vardı.

Birincisi benim hatamdı - konteynere referans vermek için yapıcı enjeksiyon kullanıyordum ve bu yapıcıda da sınıfın yeni örneğini ileride kullanmak üzere konteynere ekliyordum. Bu geriye dönük yaklaşım, ömür boyu yöneticisini Transient'ten ContainerControlled olarak değiştirmenin etkisine sahipti, böylece GetValue adlı nesne Unity, SetValue adlı nesne ile aynı değildi. Çıkarılan ders oluşturma sırasında nesnenin yaşam boyu yöneticisini değiştirebilecek hiçbir şey yapmayın.

İkinci senaryo, RegisterInstance her çağrıldığında UnityDefaultBehaviorExtension'ın önce GetValue çağırmadan SetValue'yu çağırmasıydı. Neyse ki, Birlik, yeterince kanlı bir bilinçle, problemi çözebilecek kadar genişletilebilir.

Bunun gibi yeni bir davranış uzantısıyla başlayın:

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

Sonra varsayılan davranışı değiştirmek için bir yol gerekir. Birliğin belirli bir uzantıyı kaldırmak için bir yöntemi yoktur, bu nedenle her şeyi kaldırmanız ve diğer uzantıları yeniden yerine koymanız gerekir:

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

Bu UnityClearBuildPlanStrategies? RemoveAllExtensions, biri hariç tüm kapsayıcıların dahili politika ve strateji listelerini temizler; bu nedenle, varsayılan uzantıları geri yüklerken çoğaltmalar eklememek için başka bir uzantı kullanmak zorunda kaldım:

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

Şimdi, çılgınlığın eşiğine sürüklenme korkusu olmadan RegisterInstance'ı güvenle kullanabilirsiniz. Emin olmak için işte bazı testler:

[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 { }
38
Rory MacLeod

Sabit Unity'nin son sürümünde (2.1.505.2). NuGet ile alın.

12
Grigori Melnik

Sorunuzun cevabı ne yazık ki hayır. Bunu Microsoft kalıplar ve uygulamalar grubundaki dev ekibiyle birlikte takip ettim (yakın zamana kadar orada liderlik yaptım) ve bunu EntLib 5.0 için düşünmemiz gereken bir hata vardı. Bazı araştırmalar yaptık ve bunun kodumuz ve hata ayıklayıcı arasında beklenmeyen bazı etkileşimlerden kaynaklandığı sonucuna vardık. Düzeltmeyi düşündük ancak bunun varolan koddan daha karmaşık olduğu ortaya çıktı. Sonunda bu, diğer şeylerin altında öncelik sırasına girdi ve 5 için çıtayı yapmadı.

Üzgünüm, size daha iyi bir cevabım yok. Teselli olacaksa, onu da rahatsız edici buluyorum.

10
Ade Miller

Bu kısa çözümü kullanıyorum:

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

ve bu şekilde kullanın:

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

buradaki sorun, ContainerControlledLifetimeManager'ın temel sınıfının, monitor.innet () ile base.GetValue üzerinden bir SynchronizedSetValue beklemesini beklemesidir; .

saygılarımızla, Koen

7
Koen VV

Rory'nin çözümü harika - teşekkürler. Her gün beni rahatsız eden bir sorunu çözdüm! 

    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);
            }
        }
    }
4
Zubin Appoo

Zubin Appoo'nun cevabındaki bir hataya dikkat edin: UnityClearBuildPlanStrategies kodunda eksik. 

Doğru kod pasajı şudur:

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
user556903

Birlik 2.1 - Ağustos 2012 güncellemesi hatayı düzeltildi

  1. İş parçacığı güvenlik sorununa değinmek: Http://unity.codeplex.com/discussions/328841

  2. System.Threading.SynchronizationLockException'da hata ayıklama deneyimini geliştirme: Https://entlib.uservoice.com/forums/89245-general/suggestions/2377307-fix-the-srea-suggestions/2377307-fix-the-s-dreading-synchronizationlockexcep

  3. Bir tür yüklenemediğinde daha iyi hata mesajlaşma yoluyla hata ayıklama deneyimini geliştirme: Http://unity.codeplex.com/workitem/9223

  4. Genel bir kurucusu olmayan bir sınıfın mevcut örneğinde bir BuildUp () gerçekleştirme senaryosunu desteklemek: Http://unity.codeplex.com/workitem/9460

Güncelleme deneyimini kullanıcılar için olabildiğince basit hale getirmek ve Assembly ciltleme yönlendirmeleri gereksinimini önlemek için, .NET derleme sürümünü değil, derleme dosyası sürümünü artırmayı seçtik.

0
huoxudong125