自动处理顺序

本文关键字:顺序 处理 | 更新日期: 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方法由ILifetimeScopeDispose方法调用并'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。参见InstanceLookupActivate方法(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;
}