【问题标题】:Run an IHostedService function during the Start of application but only once在应用程序启动期间运行 IHostedService 函数,但只运行一次
【发布时间】:2021-08-11 03:26:55
【问题描述】:

每次应用程序启动时我只需要run a function once(该函数会在我的数据库中检查特定的Mongo collection 并插入我自己预定义的文档中)。

IHostedService/BackgroundService 似乎能够胜任这项工作。我只需要将服务注入到我的 Startup.cs 文件中。

但是,我想知道是否有任何方法可以优雅地完成这项任务,因为 IHostedService 真的是为了实现更多 cron job (需要在一段时间内运行的任务,比如每 30分钟)。

谢谢。

【问题讨论】:

  • 我不明白你为什么不简单地使用IHostedService?您对因为 IHostedService 确实是为了实现更多的 cron 作业的解释是绝对错误的。
  • 基本上,使用IHostedServiceBackgroundService 是完成您想做的事情的唯一正确方法。
  • 如果您只需要在应用程序启动时运行一个函数,则不需要 Backgroundjob 或 HostedService。例如,如果您使用 ASP.net Core API 项目或什至任何类型的其他项目类型,只需在 program.csstartup.cs 中调用您的方法。您可以轻松获得所需的服务。

标签: c# asp.net-core startup background-task ihostedservice


【解决方案1】:

编辑: 误解,必须在应用程序启动后执行单个任务。

有多种解决方法,但我会选择IHostApplicationLifetime::ApplicationStarted。您可以创建一个扩展方法来注册您将在启动时执行的功能。

public static class HostExtensions
{
    public static void CheckMongoCollectionOnStarted(this IHost host)
    {
        var scope = host.Services.CreateScope();
        var lifetime = scope.ServiceProvider.GetService<IHostApplicationLifetime>();
        var loggerFactory = scope.ServiceProvider.GetService<ILoggerFactory>();
        var logger = loggerFactory!.CreateLogger("CheckMongoCollectionOnStarted");
        lifetime!.ApplicationStarted.Register(
            async () =>
            {
                try
                {
                    logger.LogInformation("CheckMongoCollectionOnStarted started");
                    //TODO: add your logic here
                    await Task.Delay(2000); //simulate working
                    logger.LogInformation("CheckMongoCollectionOnStarted completed");
                }
                catch (Exception ex)
                {
                    //shutdown if fail?
                    logger.LogCritical(ex, "An error has occurred while checking the Mongo collection. Shutting down the application...");
                    lifetime.StopApplication();
                }
                finally
                {
                    scope.Dispose();
                }
            }
        );
    }
}

然后从 Program 类中调用扩展:

public class Program
{
    public static async Task Main(string[] args)
    {
        var host = CreateHostBuilder(args).Build();
        host.CheckMongoCollectionOnStarted();
        await host.RunAsync();
    }

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

【讨论】:

  • 嗨,如果我只需要在每次应用程序启动时运行一次任务并且不需要任何间隔。我应该如何实施? Task.Result() 会起作用还是应该只返回一个布尔值以指示任务已成功运行,然后传入取消令牌?
  • 对不起@Noelia 我现在明白了。好吧,您可以使用 BackgroundService、IHostApplicationLifetime::ApplicationStarted、IStartupFilter 并配置一个中间件以在标准配置之后运行,或者您可以使用您的 Program.cs 创建一个范围并运行您的检查。我将更新我的答案。
  • @Noelia,请查看更新后的答案。我希望它有所帮助。
  • 你为什么要在 leu 中这样做,而不是创建一个 IHostedService
  • @Andy IHostedService:StartAsync(CancellationToken)Startup.Configure 和服务器启动之前(IApplicationLifetime.ApplicationStarted)被调用。您可以更改此行为,但您需要修改 CreateDefaultBuilder 函数。此外,您需要在Startup.ConfigureServices 上注册IHostedServiceIHostedService 通常用于运行较长的后台任务,在这种情况下,@Noelia 希望在应用程序启动后运行单个任务。
【解决方案2】:

我能够通过使用 IHostedService 来实现我想要的。

protected override async Task ExecuteAsync(CancellationToken cancellationToken)
        {
            //logic
        }

在 Startup.cs 中,这就是我注册服务的方式。

AddSingleton<IHostedService, myService>

我运行了我的应用程序,它在 AddSingleton 行中进行了调试,并且只运行了一次 ExecuteAsync 函数。这就是我的解决方案。

【讨论】:

  • 你需要这样称呼它:services.AddHostedService&lt;MyHostedService&gt;()。然后框架将正确执行它。
猜你喜欢
  • 2022-01-05
  • 2020-08-03
  • 2019-04-15
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多