Unity 2.0和处理IDisposabletypes(尤其是使用PerThreadLifetimeManager)

我知道类似的问题被问了几次(例如: 这里 , 这里 , 这里和这里 ),但它是以前版本的Unity,答案取决于使用的LifetimeManager类。

文档说:

Unity使用从LifetimeManager基类inheritance的特定types(统称为生存期pipe理器)来控制它如何存储对对象实例的引用以及容器如何处理这些实例。

好吧,听起来不错,所以我决定在生命周期pipe理中检查构build的实现。 我的结论是:

  • TransientLifetimeManager – 不处理处理。 容器只能parsing实例,并不会跟踪它。 调用代码负责处理实例。
  • ContainerControlledLifetimeManager – configuration生命期pipe理器时的实例(=configuration容器时)。 提供hiearchy中所有容器共享的单例实例。
  • HierarchicalLifetimeManager – 从ContainerControlledLifetimeManager派生行为。 它在hiearchy(子容器)中为每个容器提供“单例”实例。
  • ExternallyControlledLifetimeManager – 不处理处理。 正确的行为,因为容器不是实例的所有者。
  • PerResolveLifetimeManager – 不处理处理。 它通常与TransientLifetimeManager相同,但它允许在parsing整个对象图时重用dependency injection的实例。
  • 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; } } 

因此,处置容器不会处理由此终身pipe理器创build的一次性实例。 线程完成也不会处理这些实例。 那么谁来负责释放实例?

我试图手动处理解决的代码实例,我发现另一个问题。 我不能拆解这个办公室。 终身pipe理器的RemoveValue是空的 – 一旦实例被创build,它是不可能从线程静态字典中删除它(我也怀疑TearDown方法什么都不做)。 因此,如果在处理实例后调用Resolve ,则将获得处置实例。 我认为使用线程池中的线程使用此生存pipe理器时,这可能是相当大的问题。

如何正确使用这个一生的经理?

而且这个实现经常在PerCallContext,PerHttpRequest,PerAspNetSession,PerWcfCall等自定义生命周期pipe理器中重用。只有线程静态字典被replace为其他一些构造。

我也正确地理解处理一次性物品是否依赖于终生pipe理者? 所以应用程序代码依赖于使用的生命期pipe理器。

我读过在其他IoC容器中处理临时一次性对象是由子容器处理的,但我没有findUnity的例子 – 它可能可以用局部范围的子容器和HiearchicalLifetimeManager处理,但我不知道该怎么做。

只有less数情况下Unity会处理一个实例。 它真的不受支持。 我的解决scheme是实现此目的的自定义扩展 – http://www.neovolve.com/2010/06/18/unity-extension-for-disposing-build-trees-on-teardown/

看看Unity 2.0的源代码,它闻起来像LifetimeManagers用来以不同的方式保持对象的范围,所以垃圾收集器不会摆脱它们。 例如,使用PerThreadLifetimeManager,它将使用ThreadStatic在每个具有该特定线程的生命周期的对象上保存一个引用。 但是,在容器处置之前,它不会调用Dispose。

有一个LifetimeContainer对象用于保存所有创build的实例,然后在UnityContainer处置时进行Disposed(反过来按照时间顺序排列所有IDisposables)。

编辑:经仔细检查,LifetimeContainer只包含LifetimeManagers(因此名称“Lifetime”Container)。 所以当它被处置时,它只能处理终身pipe理者。 (我们面临的问题已经讨论过了)。

是否可以使用HttpContext.Current.ApplicationInstance.EndRequest事件挂钩到请求的末尾,然后丢弃存储在此生存pipe理器中的对象? 像这样:

 public HttpContextLifetimeManager() { HttpContext.Current.ApplicationInstance.EndRequest += (sender, e) => { Dispose(); }; } public override void RemoveValue() { var value = GetValue(); IDisposable disposableValue = value as IDisposable; if (disposableValue != null) { disposableValue.Dispose(); } HttpContext.Current.Items.Remove(ItemName); } public void Dispose() { RemoveValue(); } 

您不必像使用其他解决scheme那样使用子容器,并且用于处理对象的代码仍然处于终身pipe理器中。

我最近自己遇到了这个问题,因为我正在将Unity应用到我的应用程序中。 我在Stack Overflow和其他在线上find的解决scheme似乎没有以令人满意的方式解决这个问题,我认为。

不使用Unity时,IDisposable实例有一个很好理解的使用模式:

  1. 在一个小于函数的范围内,把它们放在一个using块中以便“免费”处理。

  2. 当为类的实例成员创build时,在类中实现IDisposable,并将其清理到Dispose()

  3. 当传入一个类的构造函数时,不要做任何事情,因为IDisposable实例被其他地方所拥有。

Unity混淆了事情,因为当dependency injection正确完成时,上面的情况#2就消失了。 所有依赖关系都应该被注入,这意味着基本上没有类将拥有正在创build的IDisposable实例。 但是,它也没有提供一种方法来“获取”在Resolve()调用期间创build的IDisposables,所以似乎不能using块。 剩下什么选项?

我的结论是, Resolve()接口本质上是错误的。 只返回需要特殊处理的请求types和泄漏对象(如IDisposable)不能正确。

作为回应,我为Unity编写了IDisposableTrackingExtension扩展,该扩展跟踪在typesparsing期间创build的IDisposable实例,并返回包含请求types实例和来自对象图的所有IDisposable依赖项的一次性包装器对象。

有了这个扩展,typesparsing看起来像这样(这里使用工厂显示,因为您的业务类不应该把IUnityContainer作为依赖):

 public class SomeTypeFactory { // ... take IUnityContainer as a dependency and save it IDependencyDisposer< SomeType > Create() { return this.unity.ResolveForDisposal< SomeType >(); } } public class BusinessClass { // ... take SomeTypeFactory as a dependency and save it public void AfunctionThatCreatesSomeTypeDynamically() { using ( var wrapper = this.someTypeFactory.Create() ) { SomeType subject = wrapper.Subject; // ... do stuff } } } 

这从上面调和了一次性使用模式#1和#3。 正常的类使用dependency injection; 他们不拥有注入的IDisposables,所以他们不处理它们。 执行typesparsing的类(通过工厂),因为它们需要dynamic创build的对象,这些类是所有者,并且此扩展提供了pipe理处置范围的工具。