Autofac、IDisposable 和在某些情况下手动调用 .Resolve

Autofac, IDisposable and manually calling .Resolve in certain circumstances

我在此处发布了有关 IDisposable 对象和使用 Autofac 的一般指南问题:。不幸的是,我没有考虑到我们项目中的一个特定场景,这实际上是一个独立的独立问题,所以会在这里提问:

我有一个 Repository 对象,它管理其中的会话对象的生命周期。因此,Repository 对象是 IDisposable 并销毁 session(Repository 在构造时注入工厂委托,在首次使用期间实例化 session,如果 session 不为 null,则在 IDisposable 中销毁 session)。根据上面对 Whosebug 问题的引用,我知道任何用我的 Repository 对象注入的对象都不应该实现 IDisposable,因为 Autofac 将处理我的存储库的处置,如果它正在注入它们。

根据提到的 Whosebug 线程,我已经开始清理对象中的 IDisposable 用法,直到我偶然发现如下所示的 NotificationPublisher class。在一些类似的地方,classes 注入了充当工厂的 IComponentContext 的实现。解析在函数中手动发生,因为代码库直到运行时才知道需要注入什么处理程序。

public class NotificationPublisher : INotificationPublisher
{
    private readonly IComponentContext _container;
    private readonly INotificationManager _notificationManager;

    public NotificationPublisher(IComponentContext container,
        INotificationManager notificationManager) 
    {
        _container = container;
        _notificationManager = notificationManager;
    }

    public IEnumerable<IAlertSubscription> Publish(Account account,
        INotificationInitiator owner, INotificationEntity entity, 
        Int32 severity, CheckCycleContext monitoringContext) 
    {
        var alertSubscriptions =
            _notificationManager.GetAlertSubscriptions(account, owner, severity);

        foreach (var alertSubscription in alertSubscriptions)
        {
            var destination = alertSubscription.GetConsumer();

            Type handlerType = typeof (INotificationHandler<,>)
                .MakeGenericType(entity.GetType(), destination.GetType());

            using (var handler = 
                (INotificationCustomHandler)_container.ResolveOptional(handlerType))
            {
                if (handler == null) continue;

                try
                {
                    Retry.Execute(() => (handler).Send(entity, destination), 3, 500);
                    monitoringContext.Record(CheckCycleContext.CycleSeverity.Information, 
                        string.Format("NotificationPublisher.Publish:{0}/{1}", 
                            entity.GetType().Name, destination.GetType().Name), "Success");
                }
                catch (Exception ex)
                {
                    monitoringContext.Record(CheckCycleContext.CycleSeverity.Error, 
                        string.Format("NotificationPublisher.Publish:{0}/{1}", 
                            entity.GetType().Name, destination.GetType().Name), ex.Message, ex, 
                                new {entity, destination});
                }
            }
        }
        return alertSubscriptions;
    }
}

我假设由于 INotificationCustomHandler 是手动解析的,因此必须使用 using 语句手动处理它,因为 INotificationCustomHandler 的实现与注入的 IManager 的实现一起注入IRepository 的实现。

因此,在这种情况下,我需要在我的代码库中传播 IDisposable,这违背了我在之前的 SO 问题中的建议。

如何在需要时通过工厂手动解析对象,同时让 Autofac 处理处置?

Autofac 解析实现 IDisposable 的组件时,该组件将链接到您注册时配置的范围。当这个范围被释放时,所有链接的组件也将被释放。有关详细信息,请参阅 http://autofac.readthedocs.org/en/latest/lifetime/disposal.html

在您的情况下,如果 INotificationCustomHandler 注册为 InstancePerDependency(默认)或 InstancePerLifetimeScopeINotificationCustomHandler_container 解析,将在_container 也将被处理掉。

如果这是您想要的,则不必对这些组件调用 .Dispose

如果您想手动控制对象的生命周期,您可以创建自己的生命周期范围。

using(ILifetimeScope scope = this._container.BeginLifetimeScope())
{
    var handler = (INotificationCustomHandler)scope.ResolveOptional(handlerType); 
    if(handler != null) 
    {
        Retry.Execute(() => handler.Send(entity, destination));
    }
} // handler will be disposed here if needed

你也应该看看 owned instance,它就像一个迷你工厂。

if(!container.ComponentRegistry.IsRegistered(new TypedService(handlerType)))
{
    continue;
}

Type handlerFactoryType = typeof(Func<>).MakeGenericType(
                            typeof(Owned<>).MakeGenericType(handlerType)); 
var handlerFactory = (Func<Owned<INotificationCustomHandler>>)container
                           .Resolve(handlerFactoryType);

using(Owned<INotificationCustomHandler> ownedHandler = handlerFactory())
{
    INotificationCustomHandler handler = ownedHandler.Value; 
    Retry.Execute(() => handler.Send(entity, destination), 3, 500);
} // handler will be disposed here