可以使Unity一直不抛出SynchronizationLockException吗?
Unitydependency injection容器似乎是一个众所周知的问题,其中SynchronizedLifetimeManager通常会导致Monitor.Exit方法抛出一个SynchronizationLockException,然后被捕获并被忽略。 这对我来说是一个问题,因为我喜欢使用Visual Studio进行debugging,以打破任何抛出的exception,所以每当我的应用程序启动时,我都无故打断这个exception多次。
我怎样才能防止这个exception被抛出?
无论在networking上的其他地方提到这个问题,build议通常包括更改debugging器设置以忽略它。 这类似于去看医生,说:“医生,医生,当我举起arm时,我的arm会疼。”被告知:“好吧,停止抬高它。 我正在寻找一个解决scheme,停止exception被抛出首先。
在SetValue方法中发生exception,因为它假定GetValue将被首先调用,其中Monitor.Enter被调用。 但是,LifetimeStrategy和UnityDefaultBehaviorExtension类都定期调用SetValue而不调用GetValue。
我宁愿不必更改源代码并维护我自己的Unity版本,所以我希望有一个解决scheme,我可以在容器中添加一些扩展,策略或策略的组合,以确保终身pipe理器是一个SynchronizedLifetimeManager,GetValue总是在别的之前被调用。
我确信有很多方法可以调用SynchronizedLifetimeManager,或者像ContainerControlledLifetimeManager这样的后代,但是有两种情况会导致我的问题。
第一个是我自己的错 – 我使用构造函数注入来提供对容器的引用,并且在构造函数中,我还将该类的新实例添加到容器以供将来使用。 这种倒退方法的效果是,将生命周期pipe理器从“瞬变”更改为“容器控制”,这样,名为“GetValue”的Unity对象就不是它所称为“SetValue”的对象。 学到的教训是在构build过程中不要做任何事情,这可能会改变对象的生命周期pipe理器。
第二种情况是,每次调用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,而不用担心被赶到疯狂的边缘。 可以肯定的是,这里有一些testing:
[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 { }
在最新版本的Unity(2.1.505.2)中修复。 通过NuGet获取。
不幸的是,你的问题的答案是否定的。 我跟随微软模式与实践小组的开发团队(我是开发人员直到最近)的开发团队跟进了这个工作,我们把这个作为了EntLib 5.0的一个bug。 我们做了一些调查,得出这样的结论:这是由我们的代码和debugging器之间的一些意想不到的相互作用造成的。 我们确实考虑了一个修补程序,但事实certificate这比现有的代码更复杂。 最后,这个优先级低于其他事情,并没有为5。
对不起,我没有更好的答案给你。 如果有任何安慰,我也觉得它很刺激。
我用这个简短的解决scheme:
/// <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());
问题是ContainerControlledLifetimeManager的基类期望SynchronizedSetValue通过base.GetValue执行monitor.Enter(),但是ContainerControlledLifetimeManager类未能做到这一点(显然,它的开发人员没有'exception中断')? 。
问候,Koen
罗里的解决scheme是伟大的 – 谢谢。 解决了每天烦恼我的问题! 我对Rory的解决scheme做了一些小的调整,以便处理任何扩展注册(在我的情况下,我有一个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); } } }
在Zubin Appoo的回答中要小心一个错误:他的代码中缺lessUnityClearBuildPlanStrategies 。
正确的代码片段是:
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); } }
统一2.1 – 2012年8月更新修复错误
-
解决线程安全问题: http : //unity.codeplex.com/discussions/328841
-
改进System.Threading.SynchronizationLockException的debugging体验: https : //entlib.uservoice.com/forums/89245-general/suggestions/2377307-fix-the-system-threading-synchronizationlockexcep
-
无法加载types时,通过更好的错误消息提高debugging体验: http : //unity.codeplex.com/workitem/9223
-
支持在没有公共构造函数的类的现有实例上执行BuildUp()的scheme: http : //unity.codeplex.com/workitem/9460
为了使用户的更新体验尽可能简单,并避免使用程序集绑定redirect,我们select只增加程序集文件版本,而不是.NET程序集版本。
这可能会帮助你:
- 转到debugging – >例外…
- find真正让你感到难过的exception,例如SynchronizationLockException
瞧。