首页 > 解决方案 > 在 `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。

标签: autofac

解决方案


简短版本:谨慎使用IStartable.Start()AutoActivate和构建回调。如果您需要控制顺序,而不是使用回调和可启动项的组合,而是在构建容器后在应用程序代码中以适当的顺序执行您需要的操作。使用构建回调来运行您特定排序的逻辑,而不是尝试确保所有这三件事的特定顺序。

长版:一般来说,当前的逻辑是运行IStartable,然后AutoActivate,然后构建回调。

这是今天的逻辑,不保证明天会是这样的逻辑。

不一定能保证这一点的原因:

  • 如果一个IStartable依赖于另一个IStartable,它们将按依赖顺序运行(依赖在消耗依赖的事物之前启动)。
  • 如果IStartableorAutoActivate尝试在Startor 激活期间创建子生命周期范围并开始解决问题,那将取消订单。(是的,这是我们最近提交的一个问题。人们这样做。)
  • 构建回调的概念IStartable并且AutoActivate在某种程度上符合构建回调的概念,因此这些和/或其他启动“容器构建”逻辑的逻辑可能会被重构/移动以成为构建回调,这可能会影响排序。

就个人而言,我不使用任何这些东西。它们是用于将应用程序启动逻辑与容器创建过程结合在一起的便捷机制,但它通过选择具有不相关逻辑的依赖设置机制在某种程度上打破了单一责任原则。这可能不是这里发生的事情,但它“在野外”经常发生。诸如“我需要控制事物运行的顺序,除了事物需要运行的依赖顺序”之类的陷阱确实使人们陷入困境。

无论如何,如果它不工作或者它以你不期望的顺序运行,请考虑OnActivating结合 withSingleInstance这样的事情,这样它就会更懒惰地发生;或者将一些初始化逻辑从容器构建中移出并移到您的应用程序的特定逻辑中,您可以在其中手动控制该顺序。


推荐阅读