【问题标题】:ASP.NET Core in a Worker Service doesn't call Configure IApplicationBuilder工作器服务中的 ASP.NET Core 不调用配置 IApplicationBuilder
【发布时间】:2020-05-12 22:15:17
【问题描述】:

我正在将 ASP.NET Core 应用程序 迁移到 Worker Service 模板,并打算保留 Startup 代码。 然而在

之后

Program 中,我按照 MS Docs 中的说明保留了 CreateHostBuilder

public static void Main(string[] args)
{
    CreateHostBuilder(args).Build().Run();
}

public static IHostBuilder CreateHostBuilder(string[] args) =>
    Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureWebHostDefaults(
            webBuilder =>
            {
                webBuilder.UseStartup<Startup>();
            })
        .ConfigureServices(
            services =>
            {
                services.AddHostedService<Worker>();
            });

调试时,ConfigureServices 被调用

public void ConfigureServices(IServiceCollection services)

但启动中的Configure 未到达/调用

public void Configure(IApplicationBuilder app)

在它崩溃之前调用Run()

我也试过这个,结果是一样的:

public static IHostBuilder CreateHostBuilder(string[] args)
    => Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureWebHostDefaults(webBuilder => webBuilder.Configure(Startup.Configure))
        .ConfigureServices(
            services =>
            {
                Startup.ConfigureServices(services);
                services.AddHostedService<Worker>();
            });

有趣的是,下面的代码实际上调用了Startup.Configure(IApplicationBuilder app)

public static IHostBuilder CreateHostBuilder(string[] args)
    => Host.CreateDefaultBuilder(args)
        .UseWindowsService()
        .ConfigureWebHostDefaults(webBuilder => webBuilder.Configure(Startup.Configure));

只要我添加了ConfigureServices,它就会跳过IApplicationBuilder 配置调用。

我是否遗漏了什么或者建议的方法是什么?

编辑:

确切的错误是:

InvalidOperationException:无法解析服务类型 'Microsoft.AspNetCore.Builder.IApplicationBuilder' 尝试 激活“Kledex.Extensions.KledexAppBuilder”。

堆栈跟踪:

在 Microsoft.Extensions.DependencyInjection.ServiceProvider..ctor(IEnumerable'1 serviceDescriptors、ServiceProviderOptions 选项)在 Microsoft.Extensions.DependencyInjection.ServiceCollectionContainerBuilderExtensions.BuildServiceProvider(IServiceCollection 服务,ServiceProviderOptions 选项)在 Microsoft.Extensions.DependencyInjection.DefaultServiceProviderFactory.CreateServiceProvider(IServiceCollection 容器生成器)在 Microsoft.Extensions.Hosting.Internal.ServiceFactoryAdapter'1.CreateServiceProvider(对象 容器生成器)在 Microsoft.Extensions.Hosting.HostBuilder.CreateServiceProvider() 在 Microsoft.Extensions.Hosting.HostBuilder.Build()

当它到达CreateHostBuilder(args).Build().Run(); 并尝试解析注册的服务时,错误就会发生,而上面的错误依赖于Startup.Configure() 方法中的某些配置app.UseSomething();

Startup.Configure() 中的断点未命中。

【问题讨论】:

  • 崩溃时显示错误信息。通常 Startup 中的代码在第一个请求到来之前不会执行
  • 我添加了错误详细信息等。 Startup 在应用程序运行后立即执行,这当然是有道理的,因为 Startup 中的服务注册发生在 before 服务和请求依赖项。您可能要解决的是服务和依赖项的解析,一旦请求它们就会发生。在示例控制器和 DI 中。然而,这里的问题是 Startup.Configure(IApplicationBuilder app, IWebHostEnvironment env) 永远不会被调用,即使在请求时也是如此。
  • 我的意思是在普通的网络应用程序中,Startup.Configure 直到第一个请求才被调用。无论如何,您的错误消息表明它在该点之前都失败了,因为它无法创建似乎依赖于 IApplicationBuilder 的 KledexAppBuilder 实例,我非常肯定后者不会直接添加到 DI 容器中(因此他们的方法UseKledex ...我认为)
  • 就是这样,但是像public static IWebHostBuilder CreateWebHostBuilder(string[] args) =&gt; WebHost.CreateDefaultBuilder(args).UseStartup&lt;Startup&gt;().UseIISIntegration() 这样的效果很好。因此,Builder 的生命周期或需要配置的方式必须有所不同。
  • 我认为这可能取决于您尝试使用 KledexAppBuilder 的位置。是在worker服务中吗?

标签: c# asp.net-core .net-core-service-worker


【解决方案1】:

这是你的问题:

您正在尝试使用专为 ASP.NET Core 设计的模式,但您没有使用 ASP.NET Core,因此您没有获得 Startup 模式或它的任何功能,例如如IApplicationBuilderIWebHostEnvironment

不久前,有人问我们是否可以弄清楚如何在不使用 ASP.NET Core 的情况下将 ASP.NET Core 的 Startup 模式应用到使用通用主机构建器的东西中。 I came up with an answer.

上面的答案没有给你Configure(IApplicationBuilder app, IWebHostEnvironment env) 方法,它不应该。该方法专门用于 Web 主机。不是工人。 您没有理由需要此方法除非您正在启动一个虚拟主机。

如果是这样,那么您应该重新开始并创建一个全新的 ASP.NET Core 应用程序...不是 Worker。这会将您的应用程序配置为使用 Microsoft.NET.Sdk.Web SDK 而不是 Microsoft.NET.Sdk.Worker SDK,从而为您提供通过 Worker 路线丢失的功能。

您将不会得到对您有用的答案,除非您从 Worker SDK 迁移到 ASP.NET Core SDK。也许有人会想出一些东西,但很可能它会是一个拼凑。并且kludges会加时赛并且是一个需要维护的PITA。

Microsoft.NET.Sdk.Web 用于Windows 服务没有任何问题。微软连shows you how(具体关注Deployment Type section)。

【讨论】:

    【解决方案2】:

    鉴于您正在尝试配置 Web 主机默认值并使用 ASP.NET Core 功能(如 IApplicationBuilder),我相信您正计划在某种 Web 服务器上运行该服务。这是正确的吗?

    如果是这样,除了其他好的建议之外,我还有另一个建议,那就是继续使用 ASP.NET Core 应用程序 - 显然可以几乎按原样使用 Startup(使用 @ 987654325@ 和 Configure 方法)以及您以前的大部分应用程序。

    为此,您可以在Worker csproj 中将&lt;Project Sdk="Microsoft.NET.Sdk.Worker"&gt; 替换为&lt;Project Sdk="Microsoft.NET.Sdk.Web"&gt;

    然后,您可以配置您的CreateHostBuilder,例如:

    public static IHostBuilder CreateHostBuilder(string[] args) =>
        Host.CreateDefaultBuilder(args)
            .ConfigureWebHostDefaults(
                webBuilder =>
                {
                    webBuilder.UseStartup<Startup>();
                });
    }
    

    请注意,我故意删除了UseWindowsService(),因为我描述的是使用Worker services 而不是具体Windows services 的情况。

    现在您可以继续使用您的Startup 类,添加后台服务的注册:

    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            // ...
        }
        
        public void ConfigureServices(IServiceCollection services)
        {
            // ...
            services.AddHostedService<Worker>();
        }
    
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            // ...
        }
    }
    

    希望这会有所帮助 :) 但是,如果您特别想开发 Windows services 而不是 Worker services,请告诉我们。

    在这种情况下,我们继续拥有一个 ASP.NET Core WebApp/WebAPI 及其所有功能。我们刚刚为其添加了后台服务:)

    【讨论】:

    • 这似乎是最好的答案。我猜,但如果 OP 真的 需要 IApplicationBuilder,那么他们需要一个带有后台调度程序的 Web 服务,而不是一个包含一起启动的工作服务。
    • 谢谢 :) 也许这两个选项可以解决或多或少相同的用例,是的......我已经看到了一些关于使用工作服务的很好的例子,比如在容器中运行的 Web 应用程序,后台工作人员执行异步任务,例如发布事件或从其他服务/消息代理获取信息。这也允许对工作服务使用可观察性工具,例如健康端点和遥测,以及利用容器编排功能(例如在 Kubernetes 中)。这在使用 SOA 和微服务时变得非常有用:)
    【解决方案3】:

    是的,我遇到了同样的问题。

    回答我们可以在 Microsoft 文档here 中找到的问题。

    我们需要使用ConfigureAppConfiguration 方法作为主机服务。

    请查看我的代码示例。我们使用这段代码,它工作正常:


    Program.cs

    public class Program
    {
        public static void Main(string[] args)
        {
            CreateHostBuilder(args).Build().Run();
        }
    
        public static IHostBuilder CreateHostBuilder(string[] args) =>
            Host.CreateDefaultBuilder(args)
                .UseWindowsService()
                .ConfigureAppConfiguration((context, builder) =>
                {
                    var environment = context.HostingEnvironment.EnvironmentName;
                    builder.AddAppSettings(environment);
                })
                .ConfigureWebHostDefaults(builder =>
                {
                    builder.UseStartup<Startup>();
                });
    }
    

    托管服务

    public class HostedService : IHostedService
    {
        private readonly IServiceScopeFactory _serviceScopeFactory;
        private readonly ILogger<HostedService> _logger;
    
        public HostedService(IServiceScopeFactory serviceScopeFactory, ILogger<HostedService> logger)
        {
            _serviceScopeFactory = serviceScopeFactory;
            _logger = logger;
        }
    
        public Task StartAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Service is starting...");
    
            // Run new task and use your business logic
            using (var scope = _serviceScopeFactory.CreateScope())
            {
                // Resolve business manager class with many dependencies. It is Scoped LifeTime.
                var manager = scope.ServiceProvider.GetRequiredService<IClientManager>();
            }
    
            return Task.CompletedTask;
        }
    
        public Task StopAsync(CancellationToken cancellationToken)
        {
            _logger.LogInformation("Service is stopping...");
            return Task.CompletedTask;
        }
    }
    


    所以我们使用这种方法只有一个问题,我们只能将单例注入HostedService 类。我们将IServiceScopeFactory 注入构造函数。然后我们创建范围,解决所有需要的依赖关系并运行我们的业务逻辑。

    【讨论】:

      【解决方案4】:

      您可以将 HostedService 的配置从 Program 类移至 Startup。只需在调用 AddControllers 之前将其添加到 ConfigureServices 方法中:

      services.AddHostedService<Worker>();
      

      我以这种方式将 HostedService 包含在我的项目中,并且它可以工作。

      这是我的标准 CreateHostBuilder:

      public static IHostBuilder CreateHostBuilder(string[] args) =>
              Host.CreateDefaultBuilder(args)
                  .ConfigureWebHostDefaults(webBuilder =>
                  {
                      webBuilder.UseStartup<Startup>();
                  });
      

      【讨论】:

      • 您是否阅读了上面的示例代码?这正是我所做的,但 Configure(IApplicationBuilder app) 在运行时没有被调用。
      猜你喜欢
      • 1970-01-01
      • 2019-12-06
      • 1970-01-01
      • 2022-01-15
      • 1970-01-01
      • 2021-08-08
      • 2020-07-30
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多