我知道类似的问题被多次询问(例如:here,here,here和here)但是它适用于以前版本的Unity答案取决于使用的LifetimeManager
类。
文档说:
Unity使用继承的特定类型 来自LifetimeManager基类 (统称为终身 经理)控制它的存储方式 对象实例的引用以及如何 容器处理这些 实例
好的,听起来不错所以我决定检查终身经理中构建的实现。我的结论是:
TransientLifetimeManager
- 没有处理处理。 Container仅解析实例,并且不会跟踪它。调用代码负责处理实例。ContainerControlledLifetimeManager
- 处置生命周期管理器时的实例(=放置容器时)。提供在层次结构中所有容器之间共享的单例实例。HierarchicalLifetimeManager
- 从ContainerControlledLifetimeManager
派生行为。它在hiearchy(子容器)中为每个容器提供“单例”实例。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
来处理但是我不确定怎么做。
答案 0 :(得分:5)
Unity只会处理一个实例。它真的不受支持。我的解决方案是实现此目的的自定义扩展 - http://www.neovolve.com/2010/06/18/unity-extension-for-disposing-build-trees-on-teardown/
答案 1 :(得分:2)
查看Unity 2.0源代码,它有点像LifetimeManagers用于以不同的方式将对象保留在范围内,因此垃圾收集器无法摆脱它们。例如,使用PerThreadLifetimeManager,它将使用ThreadStatic来保存对具有该特定线程生命周期的每个对象的引用。但是,在容器处理完毕之前,它不会调用Dispose。
有一个LifetimeContainer对象用于保存所有创建的实例,然后在UnityContainer处理时被处置(反过来,按反向时间顺序排列其中的所有IDisposable)。 击>
编辑:经过仔细检查,LifetimeContainer仅包含LifetimeManagers(因此名称为“Lifetime”容器)。因此,当它被Disposed时,它只处理终身经理。 (我们面临已经讨论过的问题)。
答案 2 :(得分:1)
使用HttpContext.Current.ApplicationInstance.EndRequest事件挂钩到请求的末尾然后处理存储在此生命周期管理器中的对象是否是一个可行的解决方案?像这样:
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();
}
你不必像其他解决方案一样使用子容器,用于处理对象的代码仍然在生命周期管理器中,就像它应该的那样。
答案 3 :(得分:0)
我最近自己遇到了这个问题,因为我正在将Unity纳入我的应用程序。在我看来,我在Stack Overflow和其他网上找到的解决方案似乎并没有以令人满意的方式解决这个问题。
当不使用Unity时,IDisposable实例具有易于理解的使用模式:
在小于函数的范围内,将它们放在using
块中以便免费处理&#34;
为类的实例成员创建时,在类中实现IDisposable并将清理放在Dispose()
中。
当传递给类的构造函数时,不执行任何操作,因为IDisposable实例在其他地方拥有。
Unity混淆了一些事情,因为当依赖注入正确完成时,上面的情况#2就会消失。应该注入所有依赖项,这意味着基本上没有类将拥有正在创建的IDisposable实例。然而,它也没有提供一种方法来获得&#34;在Resolve()
调用期间创建的IDisposs,因此似乎无法使用using
块。还剩下什么选择?
我的结论是Resolve()
接口本质上是错误的。仅返回请求的类型和泄漏需要特殊处理的对象(如IDisposable)是不正确的。
作为回应,我编写了Unity的IDisposableTrackingExtension扩展,它跟踪在类型解析期间创建的IDisposable实例,并返回一个一次性包装器对象,其中包含所请求类型的实例以及来自对象的所有IDisposable依赖项曲线图。
使用此扩展,类型解析如下所示(此处显示使用工厂,因为您的业务类不应该将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
}
}
}
这可以协调上面的IDisposable使用模式#1和#3。普通类使用依赖注入;他们没有注射IDisposables,所以他们不会处理它们。执行类型解析(通过工厂)的类,因为它们需要动态创建的对象,这些类是所有者,并且此扩展提供了管理处理范围的工具。