自动处理顺序
本文关键字:顺序 处理 | 更新日期: 2023-09-27 18:11:47
我开始在我的一个项目中使用Autofac的DI,有一件事我不能从文档/谷歌中弄清楚(也许我错过了一些东西)。
根据文档,当容器(或LifetimeScope)被处置时,Autofac会自动处置在该容器/作用域中解析的所有Disposable实例。
问题是:这些实例是否按照某种特定的顺序(这是官方保证的)处理?
似乎很自然地期望,如果某些Client
实例注入了对Service
实例的引用,那么Client
应该在 Service
之前被处置。(让我们假设依赖关系图没有循环引用,这样的顺序可以被正确定义)。
如果这不是真的,并且依赖图节点可能以任意顺序排列,这意味着我必须在组件实现中采取一些额外的预防措施,以便每个组件在其某些依赖项突然失效时能够正常工作。这使得生活更加艰难。
我做了一组简单的测试,在(几乎)所有的场景中,处理的顺序确实是"自然"的。
我还浏览了Autofac的源码,发现所有自动丢弃实例都存储在堆栈内部,并按照pop()顺序(即与存储顺序相反)进行处置,这使我相信实际上强制执行了一些特定的处置顺序。
有人能评论一下吗?谢谢你。
EDIT当我试图使用PropertiesAutowired()
(通过挂钩OnActivated
事件)进行属性注入时,会发生违反"自然"处置顺序的情况。以下代码:
class Service : IDisposable
{
public Service()
{
Console.WriteLine("Service.ctor()");
}
public void Dispose()
{
Console.WriteLine("Service.Dispose()");
}
}
class Client : IDisposable
{
public Service Service { get; set; }
public Client()
{
Console.WriteLine("Client.ctor()");
}
public void Dispose()
{
Console.WriteLine("Client.Dispose()");
}
}
class Program
{
static void Main(string[] args)
{
ContainerBuilder builder = new ContainerBuilder();
builder.RegisterType<Service>();
builder.RegisterType<Client>().PropertiesAutowired();
using (var container = builder.Build())
{
var clientProp = container.Resolve<Client>();
}
}
}
产生以下输出:
Client.ctor()
Service.ctor()
Service.Dispose()
Client.Dispose()
您是对的,每个一次性组件的处置顺序与它们创建的顺序相反。
每个ILifetimeScope
都有一个IDisposer
(dispose .cs)实例,它跟踪其作用域内所有IDisposable
对象的实例。
/// <summary>
/// Provided on an object that will dispose of other objects when it is
/// itself disposed.
/// </summary>
public interface IDisposer : IDisposable
{
/// <summary>
/// Adds an object to the disposer. When the disposer is
/// disposed, so will the object be.
/// </summary>
/// <param name="instance">The instance.</param>
void AddInstanceForDisposal(IDisposable instance);
}
IDisposer
(dispose .cs)的默认实现使用Stack<IDisposable>
, Dispose
方法由ILifetimeScope
的Dispose
方法调用并'pop'堆栈。
/// <summary>
/// Releases unmanaged and - optionally - managed resources
/// </summary>
/// <param name="disposing">
/// <c>true</c> to release both managed and unmanaged resources;
/// <c>false</c> to release only unmanaged resources.
/// </param>
protected override void Dispose(bool disposing)
{
if (disposing)
{
lock (_synchRoot)
{
while (_items.Count > 0)
{
var item = _items.Pop();
item.Dispose();
}
_items = null;
}
}
base.Dispose(disposing);
}
在组件实例化之后调用AddInstanceForDisposal
。参见InstanceLookup
的Activate
方法(instancellookup .cs)
private object Activate(IEnumerable<Parameter> parameters)
{
ComponentRegistration.RaisePreparing(this, ref parameters);
try
{
_newInstance = ComponentRegistration.Activator.ActivateInstance(this, parameters);
}
catch (Exception ex)
{
throw new DependencyResolutionException(ex);
}
if (ComponentRegistration.Ownership == InstanceOwnership.OwnedByLifetimeScope)
{
// The fact this adds instances for disposal agnostic of the activator is
// important. The ProvidedInstanceActivator will NOT dispose of the provided
// instance once the instance has been activated - assuming that it will be
// done during the lifetime scope's Disposer executing.
var instanceAsDisposable = _newInstance as IDisposable;
if (instanceAsDisposable != null)
_activationScope.Disposer.AddInstanceForDisposal(instanceAsDisposable);
}
ComponentRegistration.RaiseActivating(this, parameters, ref _newInstance);
return _newInstance;
}