Autofac:处理后续创建服务抛出异常时未调用的已创建服务

问题描述 投票:0回答:1

我有以下最小示例来重现我的现实生活应用程序的问题:

public class ServiceA : IStartable, IDisposable {
    public required ServiceB ServiceB { get; set; }
    
    public void Start() {
        ServiceB.Connect();
    }

    public void Dispose() {
        Console.WriteLine("ServiceA disposed");
    }
}

public class ServiceB : IDisposable {
    public void Connect() {
        throw new TimeoutException();
    }
    
    public void Dispose() {
        Console.WriteLine("ServiceB disposed");
    }
}

和控制台应用程序:

var builder = new ContainerBuilder();

builder.RegisterType(typeof(ServiceA)).AsSelf().As<IStartable>().SingleInstance();
builder.RegisterType(typeof(ServiceB)).AsSelf().InstancePerDependency();

var container = builder.Build();

Console.ReadKey();

container.Dispose();

Console.ReadKey();

从依赖链来看,ServiceB 是 ServiceA 的依赖项,因此首先在 Build() 上实例化。在 ServiceA 的 Start() 期间,调用 ServiceB 上的 Connect() 并抛出异常。异常被转发到 Build(),该方法失败并且未创建容器。

我对阅读 Autofac 文档的期望:

自动处置 要利用自动确定性处置,您的组件必须实现 IDisposable。然后,您可以根据需要注册组件,并在解析组件的每个生命周期结束时,将调用组件上的 Dispose() 方法。

可能会调用创建的 ServiceB 上的 Dipose()。不是这种情况。 ServiceB 位于我的应用程序中的某个位置,我无法处置它,因为 IContainer 尚未创建。

ChatGPT 建议捕获异常并稍后在 IContainer 上调用 Dispose()。这意味着,我需要检查 ServiceB 上的 Connect() 是否成功(property bool IsConnected),如果没有,我调用 Dispose()。

当然,这可以解决我的问题。但同样,我的期望是 Autofac 通过 LifetimeScope 为我处理这个问题。

c# autofac
1个回答
0
投票

这是关于使用

IStartable
的一个有趣的边缘情况。我会推荐:

  1. 提出相关问题
  2. 更新您的问题的标题以反映这是非常具体的
    IStartable
    - 当前问题的标题(“Autofac:在稍后的服务创建抛出异常时处理未调用的已创建服务”)非常具有误导性。它不仅仅是任何旧服务,它非常具体地围绕在容器构建时自动实例化的事物。这将有助于其他有类似问题的人稍后找到您的问题。

顺便说一句,我发现您正在使用必需属性而不是构造函数参数进行设置。从技术上讲,您实际上不必填充所需的参数来构造对象 -

required
是语法编译器糖,而不是对象激活指南。

更具体地说:

// This won't work, it'll be caught by the compiler:
new ServiceA();

// But this totally works:
Activator.CreateInstance(typeof(ServiceA));

您应该意识到这种区别,因为并非所有 DI 框架都会为您设置所需的属性。例如,我认为 MS DI 框架目前不会这样做。如果某些东西是真正需要的,你应该将它作为构造函数参数,即使这有时会变得混乱。

© www.soinside.com 2019 - 2024. All rights reserved.