IHostBuilder.ConfigureServices bootstrap 中的简单注入器 LoggerFactory

Simple Injector LoggerFactory in IHostBuilder.ConfigureServices bootstrap

我正在使用带有通用主机 nuget 包的简单注入器,一切都很好,除了我在注册顺序上遇到了障碍。

我有一个构建器 class,它 return 有两个接口,IMessagePublisherIMessageSubscriber,这个构建器 class 还需要一个 ILoggerFactory.

我无法使用 ILoggerFactory,因为它尚未配置,并且无法调用 container.GetInstance<ILoggerFactory>,因为当代码为打电话。

我的主机在这里:

var host = CreateHostBuilder(args)
   .UseSerilog((hostContext, loggerConfiguration) =>
   {
      var setting = hostContext.Configuration
        .GetSection(Settings.Name).GetAndAssert<Settings>();

      if (setting.IsDebug == true)
          loggerConfiguration.MinimumLevel.Debug();
      else
          loggerConfiguration.MinimumLevel.Information();

      loggerConfiguration
          .MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
          .Enrich.FromLogContext()
          .WriteTo.Console()
          .WriteTo.File("logging.log");
  })
  .Build()
  .UseSimpleInjector(container);

下面是我的 CreateHostBuilder(我的问题出在 builder.Create 方法中):

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host
        .CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostContext, builder) =>
        {
            if (hostContext.HostingEnvironment.IsDevelopment())
            {
                builder.AddUserSecrets<Program>();
            }
        })
        .ConfigureServices((hostContext, services) =>
        {
            services.AddSimpleInjector(container, options =>
            {
                options.AddHostedService<Worker>();
                options.AddLogging();

                var mqttSettings = hostContext                                   
                    .Configuration
                    .GetSection(MqttSettings.Name)
                    .GetAndAssert<MqttSettings>();

                var builder = new MqttMessageBusBuilder(mqttSettings);

                builder.Create(
                    new[] { new MessagePackSerializer() },
                    new IdentifyUsingTransportHeaders(),
                    /* below is not allowed */
                    container.GetInstance<ILoggerFactory>,
                    // i also dont know where to get IloggerFactory if not yet configured
                    out var messagePublisher,
                    out var messageSubscriber,
                    mqttSettings.isDebug);

                container.RegisterInstance(messagePublisher);
                container.RegisterInstance(messageSubscriber);
            });
        });
}

我无法使用 Singleton 并将实例化推迟到需要时,因为我需要构建器 return IMessagePublisherIMessageSubscriber 实例。

否则我会使用下面的 container.GetInstance<ILoggerFactory>() 有效的地方:

// register factory to create PolygonWebsocket
container.RegisterSingleton( () => {
    var settings = container.GetInstance<Settings>();
    var secrets = container.GetInstance<Secrets>();

    return new PolygonWebsocket(
            secrets.PolygonIoApiKey,
            settings.PolygonWebSocketUrl,
            settings.PolygonReconnectTimeout,
            container.GetInstance<ILoggerFactory>()
    });

如何在 CreateHostBuilder bootstrap 期间将 ILoggerFactory 注入我的 MqttMessageBusBuilder.Create 方法?

此问题与 Simple Injector 无关,因为 ILoggerFactory 来自 IServiceCollection 及其最后的 IServiceProvider,这是一个先有鸡还是先有蛋的困境。您希望在 ConfigureServices 期间将 ILoggerFactory 提供给 MqttMessageBusBuilder.Create,而 ILoggerFactory 只能在 IServiceProvider 可用时从 IServiceProvider 中解决,这是在稍后阶段。

据我所知,您可以做三件事:

  1. IServiceCollection 拉入 ILoggerFactory,因为此时的注册已经存在于 IServiceCollection
  2. 将消息接口的注册推迟到 ConfigureServices 之后(即在 Configure 内)
  3. 现在进行注册,但使用惰性初始化,以便在解析消息接口时构建。

1 的示例:

.ConfigureServices((hostContext, services) =>
{
    var factory = (ILoggerFactory)services
        .Last(s => s.ServiceType == typeof(ILoggerFactory))
        .ImplementationInstance;

    services.AddSimpleInjector(container, options =>
    {
        ...

        var builder = new MqttMessageBusBuilder(mqttSettings);

        builder.Create(
            new[] { new MessagePackSerializer() },
            new IdentifyUsingTransportHeaders(),
            factory,
            out var messagePublisher,
            out var messageSubscriber,
            mqttSettings.isDebug);

        container.RegisterInstance(messagePublisher);
        container.RegisterInstance(messageSubscriber);
    });
});

2 的示例:

var host = 
    CreateHostBuilder(args)
    .UseSerilog((hostContext, loggerConfiguration) =>
    {
        ...
    })
    .Build()
    .UseSimpleInjector(container);

var factory = host.Services.GetRequiredInstance<ILoggerFactory>();

var mqttSettings = hostContext                                   
    .Configuration
    .GetSection(MqttSettings.Name)
    .GetAndAssert<MqttSettings>();

var builder = new MqttMessageBusBuilder(mqttSettings);

builder.Create(
    new[] { new MessagePackSerializer() },
    new IdentifyUsingTransportHeaders(),
    factory,
    out var messagePublisher,
    out var messageSubscriber,
    mqttSettings.isDebug);

container.RegisterInstance(messagePublisher);
container.RegisterInstance(messageSubscriber);

return host;

3 的示例:

public static IHostBuilder CreateHostBuilder(string[] args)
{
    return Host
        .CreateDefaultBuilder(args)
        .ConfigureAppConfiguration((hostContext, builder) =>
        {
            if (hostContext.HostingEnvironment.IsDevelopment())
            {
                builder.AddUserSecrets<Program>();
            }
        })
        .ConfigureServices((hostContext, services) =>
        {
            services.AddSimpleInjector(container, options =>
            {
                ...

                var lazy = new Lazy<(
                    IMessagePublisher Publisher,
                    IMessageSubcriber Subscriber)>(() =>
                {
                    var builder = new MqttMessageBusBuilder(mqttSettings);
                
                    builder.Create(
                        new[] { new MessagePackSerializer() },
                        new IdentifyUsingTransportHeaders(),
                        container.GetInstance<ILoggerFactory>(),
                        out var messagePublisher,
                        out var messageSubscriber,
                        mqttSettings.isDebug);
                
                    return (messagePublisher, messageSubscriber);
                });

                container.RegisterSingleton(() => lazy.Value.Publisher);
                container.RegisterInstance(() => lazy.Value.Subscriber);
            });
        });
}