我相信代码中有很多种方式调用SynchronizedLifetimeManager,或者像ContainerControlledLifetimeManager这样的子类,但有两种情况特别引起了我的问题。
第一个问题是我的错 - 我正在使用构造函数注入来提供对容器的引用,并且在那个构造函数中我还将该类的新实例添加到容器中以备将来使用。这种反向的方法会改变生命周期管理器从Transient到ContainerControlled,导致Unity在GetValue上调用的对象与SetValue上调用的对象不同。得出的教训是不要在构建期间做任何可能改变对象生命周期管理器的事情。
第二种情况是每次调用RegisterInstance时,UnityDefaultBehaviorExtension都会在没有先调用GetValue的情况下调用SetValue。幸运的是,Unity具有足够的可扩展性,只要努力一些,就可以解决这个问题。
从这里开始介绍一个新的行为扩展,如下所示:
public class UnitySafeBehaviorExtension : UnityDefaultBehaviorExtension
{
protected override void Initialize()
{
Context.RegisteringInstance += PreRegisteringInstance;
base.Initialize();
}
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
会清除容器内的所有策略和策略列表,除了一个之外,因此我必须使用另一个扩展来避免在恢复默认扩展时插入重复项:
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);
}
[TestMethod]
public void UnityDefaultBehaviorRaisesExceptionOnRegisterInstance()
{
Container.RegisterInstance<ITest>(this);
Assert.AreEqual(1, FirstChanceExceptions.Count);
Assert.IsInstanceOfType(FirstChanceExceptions[0], typeof(SynchronizationLockException));
}
[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 { }
UnityContainer
实例的引用,那么你应该能够在其上使用InstallCoreExtensions
方法。你应该检查EntLib是否添加了任何自己的扩展 - 如果是这样,你需要在InstallCoreExtensions
删除所有内容后将它们添加回来。 - Rory MacLeod