Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/317.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 可以使Unity始终不抛出SynchronizationLockException吗?_C#_Unity Container_Enterprise Library - Fatal编程技术网

C# 可以使Unity始终不抛出SynchronizationLockException吗?

C# 可以使Unity始终不抛出SynchronizationLockException吗?,c#,unity-container,enterprise-library,C#,Unity Container,Enterprise Library,Unity dependency injection容器似乎存在一个众所周知的问题,SynchronizedLifetimeManager通常会导致Monitor.Exit方法抛出SynchronizationLockException,然后捕获并忽略该异常。这对我来说是个问题,因为我喜欢使用Visual Studio进行调试,将其设置为在任何抛出的异常上中断,因此每次应用程序启动时,我都会多次无缘无故地中断该异常 如何防止抛出此异常? 在web上其他地方提到此问题的地方,建议通常包括更改调试器

Unity dependency injection容器似乎存在一个众所周知的问题,SynchronizedLifetimeManager通常会导致Monitor.Exit方法抛出SynchronizationLockException,然后捕获并忽略该异常。这对我来说是个问题,因为我喜欢使用Visual Studio进行调试,将其设置为在任何抛出的异常上中断,因此每次应用程序启动时,我都会多次无缘无故地中断该异常

如何防止抛出此异常?

在web上其他地方提到此问题的地方,建议通常包括更改调试器设置以忽略它。这类似于去看医生,然后说,“医生,医生,当我举起它的时候,我的手臂会痛,”被告知,“好吧,停止举起它。”我正在寻找一种解决方案,首先停止抛出异常

SetValue方法中发生异常,因为它假设将首先调用GetValue,此时调用Monitor.Enter。但是,LifetimeStrategy和UnityDefaultBehaviorExtension类都定期调用SetValue,而不调用GetValue

我不想更改源代码并维护我自己的Unity版本,因此我希望有一个解决方案,可以在容器中添加一些扩展、策略或策略的组合,以确保如果lifetime manager是SynchronizedLifetimeManager,GetValue总是在调用其他任何东西之前被调用。

这可能会帮助您:

  • 转到调试->异常
  • 找出那些让你非常不安的异常,比如SynchronizationLockException

你的问题的答案很不幸。我在微软模式和实践小组(我一直是DeV领导,直到最近)的DeV团队跟进了这一点,我们把它作为一个bug来考虑EnLIB 5。我们做了一些调查,得出结论,这是由于代码和调试器之间的一些意外交互造成的。我们确实考虑了修复,但这比现有代码复杂得多。最后,这被排在了其他事情之后,没有达到5的标准


对不起,我没有更好的答案给你。如果有什么安慰的话,我也觉得这很烦人。

我确信代码有很多方法可以调用SynchronizedLifetimeManager,或者像ContainerControlled LifetimeManager这样的后代,但是有两种情况特别给我带来了问题

第一个是我自己的错误——我使用构造函数注入来提供对容器的引用,在该构造函数中,我还将类的新实例添加到容器中以备将来使用。这种向后的方法的效果是将生存期管理器从瞬态更改为ContainerControl,从而使名为GetValue on的对象Unity与名为SetValue on的对象不同。吸取的教训是:不要在构建过程中做任何可能改变对象生命周期管理器的事情。

第二种情况是,每次调用RegisterInstance时,UnityDefaultBehaviorExtension都会调用SetValue,而不首先调用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();
        }
    }
}
private UnityContainer _container;
...
_container.RegisterInstance(instance, new LifeTimeManager());
请注意,
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();
    }
}
//
///实现一个用于清除
///构建容器所持有的计划策略。
/// 
公共类UnityClearBuildPlanStrategies:UnityContainerExtension
{
受保护的覆盖无效初始化()
{
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 { }
[TestClass]
公共类UnitySafeBehaviorExtensionTests:ITest
{
专用IUnityContainer容器;
私人名单第一次机会例外;
[测试初始化]
public void TestInitialize()
{
容器=新的UnityContainer();
FirstChanceExceptions=新列表();
AppDomain.CurrentDomain.FirstChanceException+=FirstChanceException引发;
}
[测试清理]
公共void TestCleanup()
{
AppDomain.CurrentDomain.FirstChanceException-=引发的FirstChanceException;
}
private void FirstChanceException引发(对象发送方,FirstChanceExceptionEventArgs e)
{
FirstChanceExceptions.Add(如异常);
}
/// 
///测试UnityContainer的默认行为是否导致SynchronizationLockException
///被扔在地上。
/// 
[测试方法]
public void unitydefaultbehaviorraiseexceptionnRegisterInstance()
{
容器。注册表状态(此);
aresequal(1,FirstChanceExceptions.Count);
Assert.IsInstanceOfType(FirstChanceExceptions[0],typeof(SynchronizationLockException));
}
/// 
///测试UnitySafeBehaviorExtension是否可以防止同步锁定异常被删除
///在调用RegisterInstance时引发。
/// 
[测试方法]
公共无效安全行为或声明接受注册声明()
{
Container.RemoveAllExtensions();
Container.AddExtension(新的UnitySafeBehaviorExtension());
Container.AddExtension(newinjectedMembers());
Container.AddExtension(新的UnityDefaultStrategiesExtension());
容器。注册表状态(此);
Assert.AreEqual(0,FirstChanceExceptions.Count);
}
}
公共接口ITest{}

Rory的解决方案很棒-谢谢。解决了一个困扰我的问题
    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);
            }
        }
    }
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);
   }
}
/// <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());