【发布时间】:2011-07-05 00:38:52
【问题描述】:
我知道类似的问题被问了好几次(例如:here、here、here 和 here)但它是针对以前版本的 Unity,其答案取决于使用的 LifetimeManager类。
文档说:
Unity 使用继承的特定类型 来自 LifetimeManager 基类 (统称为寿命 经理)来控制它如何存储 对对象实例的引用以及如何 容器处理这些 实例。
好的,听起来不错,所以我决定检查内置生命周期管理器的实现。我的结论:
-
TransientLifetimeManager- 不处理处置。容器只解析实例,它不跟踪它。调用代码负责处理实例。 -
ContainerControlledLifetimeManager- 在处置生命周期管理器时处置实例(= 当处置容器时)。提供在层次结构中的所有容器之间共享的单例实例。 -
HierarchicalLifetimeManager- 从ContainerControlledLifetimeManager派生行为。它为层次结构(子容器)中的每个容器提供“单例”实例。 -
ExternallyControlledLifetimeManager- 不处理处置。正确的行为,因为容器不是实例的所有者。 -
PerResolveLifetimeManager- 不处理处置。它通常与TransientLifetimeManager相同,但它允许在解析整个对象图时重用实例进行依赖注入。 -
PerThreadLifetimeManager- 不处理处置,如 MSDN 中所述。 谁负责处置?
内置PerThreadLifetimeManager的实现是:
public class PerThreadLifetimeManager : LifetimeManager
{
private readonly Guid key = Guid.NewGuid();
[ThreadStatic]
private static Dictionary<Guid, object> values;
private static void EnsureValues()
{
if (values == null)
{
values = new Dictionary<Guid, object>();
}
}
public override object GetValue()
{
object result;
EnsureValues();
values.TryGetValue(this.key, out result);
return result;
}
public override void RemoveValue()
{ }
public override void SetValue(object newValue)
{
EnsureValues();
values[this.key] = newValue;
}
}
因此,处置容器不会处置使用此生命周期管理器创建的一次性实例。线程完成也不会处理这些实例。那么谁负责发布实例呢?
我尝试在代码中手动处理已解析的实例,但发现了另一个问题。我无法拆除实例。生命周期管理器的 RemoveValue 是空的 - 一旦创建了实例,就不可能从线程静态字典中删除它(我也怀疑 TearDown 方法什么都不做)。因此,如果您在处置实例后调用Resolve,您将得到处置实例。我认为当将此生命周期管理器与线程池中的线程一起使用时,这可能是一个很大的问题。
如何正确使用这个生命周期管理器?
此外,此实现通常在自定义生命周期管理器中重用,例如 PerCallContext、PerHttpRequest、PerAspNetSession、PerWcfCall 等。只有线程静态字典被其他构造替换。
我是否正确理解处理一次性对象取决于生命周期管理器?所以应用程序代码依赖于使用的生命周期管理器。
我读到在其他处理临时一次性对象的 IoC 容器中由子容器处理,但我没有找到 Unity 的示例 - 它可能可以使用本地范围的子容器和 HiearchicalLifetimeManager 处理,但我不确定如何去做吧。
【问题讨论】:
-
有可能通过实现自定义生命周期让 Unity 确定性地实现处置行为,但是这里解释起来太复杂了。在我的书中,我使用了超过六页来解释如何做到这一点:affiliate.manning.com/idevaffiliate.php?id=1150_236
-
@Mark:很遗憾你的书将在 8 月出版,我不喜欢 MEAP。
-
另一个有趣的点(或设计疏忽)是在内部注册
UnityContainer会在你处理它时抛出 StackOverflowException。您需要使用ExternallyControlledLifetimeManager来防止容器尝试调用它自己的 Dispose 方法。 -
@RichardDingwall 你不应该需要自己注册它,它已经可以默认解析
IUnityContainer。
标签: c# .net inversion-of-control unity-container idisposable