能否让Unity始终不抛出Synchronization LockException?

2024-01-27

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

如何防止抛出此异常?

无论网络上其他地方提到此问题,建议通常都会涉及更改调试器设置以忽略它。这类似于去找医生说:“医生,医生,我举起手臂时会疼”,然后医生告诉他:“好吧,别举了。”我正在寻找一种解决方案,可以首先阻止抛出异常。

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

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


我确信代码可以通过很多方式调用 SynchronizedLifetimeManager 或像 ContainerControlledLifetimeManager 这样的后代,但是有两种情况特别给我带来了问题。

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

第二种情况是,每次调用RegisterInstance时,UnityDefaultBehaviorExtension都会调用SetValue,而不先调用GetValue。幸运的是,Unity 具有足够的可扩展性,只要有足够的热心,您就可以解决这个问题。

从像这样的新行为扩展开始:

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

那么您需要一种方法来替换默认行为。 Unity 没有删除特定扩展的方法,因此您必须删除所有内容并再次将其他扩展放回原处:

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

现在您可以安全地使用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 { }
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

能否让Unity始终不抛出Synchronization LockException? 的相关文章

随机推荐

  • 使用 Javascript 的递归方法[重复]

    这个问题在这里已经有答案了 我正在尝试复制 json stringify 方法 但使用递归 我已经能够通过很多测试用例 但是当涉及到嵌套数组时 我似乎遇到了问题 如果数组内有任何空数组 我会得到类似 7 9 而不是 7 9 的内容 另外 如
  • 在 C 中创建字符串堆栈

    我想要一个接受字符串的堆栈 我希望能够推入和弹出字符串 以及清除整个堆栈 我认为 C 有一些方法可以做到这一点 那么C呢 未经测试的快速示例 采用单链表结构 元素被推入列表头部或从列表头部弹出 include
  • 将工作表传递给函数(excel vba)

    我一直在尝试将工作簿中的一张工作表传递给函数 但它似乎不起作用 函数是 Private Sub passToSheet theData As Variant Optional mySheet As Worksheet 我试过做 Dim my
  • 从 azure 部署的机器人向 MS 团队发送主动消息

    我已经将机器人部署到 azure 当连接到 azure 中的 MS 团队频道时 我能够 ping 机器人并接收消息 这很好 我还在机器人中添加了主动消息传递 其中通道中每隔一分钟就会触发一条消息 它在模拟器中工作 但在网络聊天和 MS 团队
  • 相当于 R 中的“this”或“self”

    我正在寻找 R 中 python 的 self 关键字或 java 的 this 关键字的等效项 在下面的示例中 我从另一个 S4 对象的方法创建一个 S4 对象 并且需要将一个指针传递给我自己 该语言中有什么东西可以帮助我做到这一点吗 M
  • 在django中扩展User模型后,如何创建ModelForm?

    我扩展了 django 中的用户模型以包含其他几个变量 例如位置和雇主 现在我正在尝试创建一个具有以下字段的表单 First name from User Last name from User Location from UserProf
  • WebAuthN 是否可以查看可用的平台验证器?

    我的任务是为 webauthn 创建自定义注册屏幕 我们想要向用户展示faceid将用于验证他们的身份 或者他们的指纹将被使用 拥有设备表并从中派生的解决方案似乎是一场可持续发展的噩梦 我更愿意从 navigator credentials
  • 使用 OIDC 在每个 API 请求中发送哪些信息

    I m writing an API back end that I want to use OpenID Connect OIDC to secure I ve been reading the documentation but I m
  • 使用 Angular 在 Firebase 中保存结构化数据

    我了解储蓄的概念和最佳实践结构化数据 https www firebase com docs web guide structuring data html在 firebase 中 但我不清楚如何将数据实际保存到多个位置并提供所需的交叉引用
  • 使用 R 映射博客之间的链接网络?

    我想了解有关如何创建和可视化博客之间的链接图以反映它们之间的 社交网络 的建议 这是我的想法 从一个 或多个 博客主页开始 收集该页面上的所有链接 删除所有属于内部链接的链接 也就是说 如果我从 www website com 开始 那么我
  • UIViewController 内的 PullToRefresh TableView

    我有一个UI视图控制器这也是UITableViewDelegate 除其他外 对于UI表格视图 在 FirstView xib 中创建 interface FirstViewController UIViewController lt UI
  • 对象数组的深拷贝

    我想使用构造函数制作对象数组的深层副本 public class PositionList private Position data new Position 0 public PositionList PositionList othe
  • 选择价格未变化的行

    假设您有一个类似的表 我使用 SQL Server 2008 没有审核日志 表很大 SecID Date Price 1 1 1 11 10 1 1 2 11 10 1 1 3 11 5 1 1 4 11 10 1 1 5 11 10 假设
  • Xcode,误删除.pch文件,可以恢复吗?

    据我了解 pch 文件负责预编译头文件 听起来像是可以重建或重新创建的东西 2个问题 Xcode 是否有类似于回收站的东西 可以撤消文件删除 有没有直接的方法来重建 pch 文件 对于 CocoaTouch iPhone 来说是 Prefi
  • IE8 CSS 浮动问题

    我确信这之前一定已经涵盖过 因为我确信我不是第一个这样做的人 但我找不到答案 我正在为一个创建阴影效果 div 使用重复的背景图像 在底部的两个角 我使用 9x5 像素的小图像 我将它们左右浮动 在 Firefox 和 Safari 中它们
  • 在工具栏右侧添加自定义视图

    我正在努力实现这一目标 计时器toolbar红色背景 I am trying to add customView in toolbar It always end s up being on extreme left just beside
  • Android重复资源错误

    我的项目编译得很好 直到我删除了一些 png 可绘制对象 并通过导航到将它们替换为矢量可绘制对象新增 gt 矢量资源 我现在得到了重复资源错误 这是我的 res 文件夹 以下是 Gradle 控制台中的错误消息 Execution fail
  • Environment.MachineName 被裁剪为 15 个字符 [重复]

    这个问题在这里已经有答案了 NET System Environment MachineName 中有一个属性 它读取由于与 Windows 98 兼容而被裁剪为 15 个字符的计算机名称 我如何获得完整 未裁剪 的姓名 我建议使用inte
  • MySQL存储引擎的困境

    我想在我的应用程序中使用两个 MySQL 数据库功能 第一个是全文搜索和交易 现在 这里的困境是我无法在一个存储引擎中获得此功能 我要么使用 MyIsam 具有全文搜索功能 要么使用 InnoDB 支持 TRANSACTION 功能 我不能
  • 能否让Unity始终不抛出Synchronization LockException?

    Unity 依赖注入容器存在一个众所周知的问题 即 SynchronizedLifetimeManager 通常会导致 Monitor Exit 方法抛出 SynchronizationLockException 然后该异常会被捕获并忽略