autofac - 在 `RegisterBuildCallback` 之前调用 Autofac `IStartable.Start()`
问题描述
组件的IStartable.Start()
方法在之前被调用RegisterBuildCallback
。它是错误还是功能?
根据文档:
可启动组件:可启动组件是在容器最初构建时由容器激活的组件
和
容器构建回调:您可以通过注册构建回调来注册在容器构建时发生的任意操作。构建回调是一个 Action,它将在从 ContainerBuilder.Build 返回该容器之前获取构建的容器。
因此,似乎没有定义操作顺序,但在我看来,容器构建回调是“容器构建过程”的一部分,只有在其他所有东西都已经构建时才应该启动组件。
复制:
using System;
using Autofac;
namespace AutofacBuildOrderRepro
{
class Program
{
static void Main(string[] args)
{
var builder = new ContainerBuilder();
builder.RegisterBuildCallback(ctx => StaticClass.ObjectProvider = () => new object());
//this fails:
builder.RegisterType<StartableClass>().As<IStartable>().AsSelf().SingleInstance();
builder.Build();
//this works:
//builder.RegisterType<StartableClass>().AsSelf().SingleInstance();
//var container = builder.Build();
//container.Resolve<StartableClass>().Start();
}
class StartableClass : IStartable
{
public void Start()
{
StaticClass.Run();
}
}
public static class StaticClass
{
public static Func<object> ObjectProvider { private get; set; }
public static void Run()
{
if (ObjectProvider== null)
{
throw new InvalidOperationException("ObjectProvider is null");
}
Console.WriteLine("Success");
}
}
}
}
异常调用堆栈:
Unhandled Exception: Autofac.Core.DependencyResolutionException: An exception was thrown while executing a resolve operation. See the InnerException for details. ---> ObjectProvider is null (See inner exception for details.) ---> System.InvalidOperationException: ObjectProvider is null
at AutofacBuildOrderRepro.Program.StaticClass.Run() in c:\path\AutofacBuildOrderRepro\Program.cs:line 39
at AutofacBuildOrderRepro.Program.StartableClass.Start() in c:\path\AutofacBuildOrderRepro\Program.cs:line 27
at Autofac.Core.Resolving.InstanceLookup.StartStartableComponent(Object instance)
at Autofac.Core.Resolving.InstanceLookup.Execute()
at Autofac.Core.Resolving.ResolveOperation.GetOrCreateInstance(ISharingLifetimeScope currentOperationScope, IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
--- End of inner exception stack trace ---
at Autofac.Core.Resolving.ResolveOperation.Execute(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Lifetime.LifetimeScope.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Core.Container.ResolveComponent(IComponentRegistration registration, IEnumerable`1 parameters)
at Autofac.Builder.StartableManager.StartStartableComponents(IComponentContext componentContext)
at Autofac.ContainerBuilder.Build(ContainerBuildOptions options)
at AutofacBuildOrderRepro.Program.Main(String[] args) in c:\path\AutofacBuildOrderRepro\Program.cs:line 15
我用 Autofac 版本试过了。4.8.1 和 3.0.4。
解决方案
简短版本:谨慎使用IStartable.Start()
、AutoActivate
和构建回调。如果您需要控制顺序,而不是使用回调和可启动项的组合,而是在构建容器后在应用程序代码中以适当的顺序执行您需要的操作。使用构建回调来运行您特定排序的逻辑,而不是尝试确保所有这三件事的特定顺序。
长版:一般来说,当前的逻辑是运行IStartable
,然后AutoActivate
,然后构建回调。
这是今天的逻辑,不保证明天会是这样的逻辑。
不一定能保证这一点的原因:
- 如果一个
IStartable
依赖于另一个IStartable
,它们将按依赖顺序运行(依赖在消耗依赖的事物之前启动)。 - 如果
IStartable
orAutoActivate
尝试在Start
or 激活期间创建子生命周期范围并开始解决问题,那将取消订单。(是的,这是我们最近提交的一个问题。人们这样做。) - 构建回调的概念
IStartable
并且AutoActivate
在某种程度上符合构建回调的概念,因此这些和/或其他启动“容器构建”逻辑的逻辑可能会被重构/移动以成为构建回调,这可能会影响排序。
就个人而言,我不使用任何这些东西。它们是用于将应用程序启动逻辑与容器创建过程结合在一起的便捷机制,但它通过选择具有不相关逻辑的依赖设置机制在某种程度上打破了单一责任原则。这可能不是这里发生的事情,但它“在野外”经常发生。诸如“我需要控制事物运行的顺序,除了事物需要运行的依赖顺序”之类的陷阱确实使人们陷入困境。
无论如何,如果它不工作或者它以你不期望的顺序运行,请考虑OnActivating
结合 withSingleInstance
这样的事情,这样它就会更懒惰地发生;或者将一些初始化逻辑从容器构建中移出并移到您的应用程序的特定逻辑中,您可以在其中手动控制该顺序。
推荐阅读
- html - 为什么我的文本是连接的,发生这种情况时它叫什么?
- c - 我的程序无法正确调整图像大小时遇到问题
- python - 如何从列表中的列表中删除项目?
- javascript - UPNG.js - 我如何处理透明图像?
- python - 为什么 p[:] 在这两种情况下的工作方式不同?
- java - 使用 KeyCloak 的多租户 Quarkus?
- javascript - 将 0 值自动填充到未定义的 xAxes
- c# - 如何在没有身份验证和授权方法的情况下保护公共端点
- powershell - 如何通过 powershell 解析最新的 DNS CName 结果
- sql - 从每个组中获取特定行