当可启动组件需要IBus时,Autofac IStartable可以和NServiceBus一起使用吗?

Can Autofac IStartable be used with NServiceBus when the startable component needs IBus?

这里的场景是有一个组件在启动时需要 运行 一些初始化代码。这是通过让代码实现 Autofac.IStartable.Start 并将代码注册为 IStartable.

来实现的

此代码随后在多个环境中使用:几个 ASP.NET 网站、一个 NServiceBus.Host.exe 托管的 NSB 应用程序和一个基于 Atlas 的 Windows 服务。它在每个方面都运行良好。

最近的一项更改为该启动代码引入了构造函数注入依赖项,其中包括一些深度 NServiceBus.IBus 的实例。当 NServiceBus 主机中的代码现在 运行s 时,它会抛出一个 ComponentNotRegisteredException 声明 "The requested service 'NServiceBus.IBus' has not been registered."

这是 NServiceBus 主机 (v4.7) 的端点配置:

var builder = new ContainerBuilder();
builder.RegisterModule(new CoreModule());
Configure.With().AutofacBuilder(builder.Build());

似乎正在发生的事情是 IStartable.Start 方法在调用 ContainerBuilder.Build() 方法时被执行,这是可以理解的,但此时 IBus 尚未注册容器。

当消息处理程序稍后通过 NServiceBus 实例化时,并且它们依赖于 IBus,总线被正确注入。这将表明 NServiceBus 是 'adding' 容器构建后对容器的新注册。

我更改了启动代码以实现 NServiceBus.IWantToRunWhenBusStartsAndStops,然后它 运行 在 NServiceBus 托管应用程序中成功地执行了 Start 方法。但是,Start 方法不会从 ASP.NET 应用程序或 Windows 服务中执行。

Web应用中的NServiceBus启动代码如下:

var bus = Configure.With((typeof (CoreModule).Assembly
    .GetTypes().Where(t =>
        t.Namespace != null
        && t.Namespace.StartsWith("OurApp.Messages"))))
    .DefaultBuilder()
    .DisableTimeoutManager()
    .UseTransport<Msmq>()
    .PurgeOnStartup(false)
    .UnicastBus()
    .CreateBus()
    .Start();

执行此代码,然后 bus 向容器构建器注册,如下所示:

builder.Register(cb => bus).As<IBus>().SingleInstance();

所以我的双重问题:Autofac.IStartable 仍然可以在 NServiceBus 托管应用程序中以某种方式使用,或者是否有一种方法可以使替代的通用 start-up/bootstrap 机制起作用,例如 NServiceBus.IWantToRunWhenBusStartsAndStops?

Can Autofac.IStartable still be used somehow in an NServiceBus hosted application?

是,但如果组件依赖于 IBus

Can NServiceBus.IWantToRunWhenBusStartsAndStops be used as an alternative?

仅当您在每个应用程序中处理 NSB 消息时。在我的例子中,该网站仅向远程端点发送消息,因此未调用此接口。

我 'solution' 确保两种情况下的启动代码 运行 是双重的:

首先,在启动时需要运行的类上实现Autofac.IStartableNServiceBus.IWantToRunWhenBusStartsAndStops。它们具有相同的方法签名,这很方便。

其次,仅在 NServiceBus.IWantToRunWhenBusStartsAndStops 调用的应用程序中在容器中注册 Autofac.IStartable 实现。这是通过将它们的注册拆分为一个单独的 Autofac 模块来完成的,该模块仅在这些应用程序中注册。该模块看起来像这样:

public class StartablesModule : Module
{
    protected override void Load([NotNull] ContainerBuilder builder)
    {
        builder.RegisterAssemblyTypes(
            typeof (StartablesModule).Assembly)
            .Where(t => t.IsAssignableTo<IStartable>())
            .As<IStartable>();
    }
}