【问题标题】:Where am I supposed to start persistent background tasks in ASP.NET Core?我应该在哪里开始 ASP.NET Core 中的持久后台任务?
【发布时间】:2018-01-10 00:48:38
【问题描述】:

在我的 Web 应用程序 (ASP.NET Core) 中,我想在后台运行一个作业,该作业正在侦听远程服务器,计算一些结果并将其推送到 Pusher(一个 websocket)上的客户端。

我不确定我应该从哪里开始这项任务。目前我在结束时开始它

    public void Configure(IApplicationBuilder app, IHostingEnvironment env)

Startup.cs

但我认为这有什么问题,在称为“配置”的方法中启动后台作业是没有意义的。我期待在某处找到 Start 方法

另外,当我尝试使用 EF Core 到 generate initial database migration file 时,它实际上会执行该方法并启动我的任务.. 这显然没有任何意义:

dotnet ef migrations add InitialCreate

从控制台运行它会创建迁移代码,该代码将用于根据我的数据模型在 SQL Server 上创建数据库。

为什么没有一种方法可以让我开始一些任务?我不希望它在一个单独的进程上,它真的不需要自己的进程,它本质上是 web 服务器的一部分,因为它确实通过 websocket 与客户端(浏览器)通信,所以它是有意义的将其作为 Web 服务器的一部分运行。

【问题讨论】:

  • 看看这个:hangfire.io
  • @zaitsman 已经在使用它了。如上所述,我正在使用 hangfire 开始我的工作。我省略了提及,因为据我了解它是无关紧要的
  • 怎么不相关?使用hangfire,您只需在对您有意义的地方进行操作?
  • 无论我是使用hangfire 还是将它们作为简单的Tasks 启动都没有关系。问题仍然存在。
  • Why isn't there a method where I can start some a Task 因为这对整个框架并不通用,这是您想要做的事情。 it doesn't make sense to start background jobs in a method called "Configure" 然后不要这样做。 I'm not sure where I'm supposed to start this task 在您的应用程序中构建代码有意义的任何地方。

标签: c# asp.net-core .net-core hangfire


【解决方案1】:

我相信你正在寻找这个

https://blogs.msdn.microsoft.com/cesardelatorre/2017/11/18/implementing-background-tasks-in-microservices-with-ihostedservice-and-the-backgroundservice-class-net-core-2-x/

我对自己进行了 2 小时自称获奖的黑客马拉松,以了解这一点。

https://github.com/nixxholas/nautilus

您可以在此处参考注入并从那里实现摘要。

许多 MVC 项目并不真正需要操作持久的后台任务。这就是为什么您看不到它们通过模板融入新项目的原因。最好为开发者提供一个界面,让他们可以点击并继续使用它。

此外,关于为此类后台任务打开该套接字连接,我还没有为此建立解决方案。据我所知/所做的,我只能将有效负载广播到连接到我自己的 socketmanager 的客户端,所以你必须在别处寻找。如果 IHostedService 中有任何关于 websockets 的信息,我肯定会发出哔哔声。

好吧,这就是发生的事情。

把它放在你的项目中,它更像是一个让你重载以创建自己的任务的界面

/// Copyright(c) .NET Foundation.Licensed under the Apache License, Version 2.0.
    /// <summary>
    /// Base class for implementing a long running <see cref="IHostedService"/>.
    /// </summary>
    public abstract class BackgroundService : IHostedService, IDisposable
    {
        protected readonly IServiceScopeFactory _scopeFactory;
        private Task _executingTask;
        private readonly CancellationTokenSource _stoppingCts =
                                                       new CancellationTokenSource();

        public BackgroundService(IServiceScopeFactory scopeFactory) {
            _scopeFactory = scopeFactory;
        }

        protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

        public virtual Task StartAsync(CancellationToken cancellationToken)
        {
            // Store the task we're executing
            _executingTask = ExecuteAsync(_stoppingCts.Token);

            // If the task is completed then return it,
            // this will bubble cancellation and failure to the caller
            if (_executingTask.IsCompleted)
            {
                return _executingTask;
            }

            // Otherwise it's running
            return Task.CompletedTask;
        }

        public virtual async Task StopAsync(CancellationToken cancellationToken)
        {
            // Stop called without start
            if (_executingTask == null)
            {
                return;
            }

            try
            {
                // Signal cancellation to the executing method
                _stoppingCts.Cancel();
            }
            finally
            {
                // Wait until the task completes or the stop token triggers
                await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite,
                                                              cancellationToken));
            }
        }

        public virtual void Dispose()
        {
            _stoppingCts.Cancel();
        }
    }

这是你如何实际使用它的方法

public class IncomingEthTxService : BackgroundService
    {
        public IncomingEthTxService(IServiceScopeFactory scopeFactory) : base(scopeFactory)
        {
        }

        protected override async Task ExecuteAsync(CancellationToken stoppingToken)
        {

            while (!stoppingToken.IsCancellationRequested)
            {
                using (var scope = _scopeFactory.CreateScope())
                {
                    var dbContext = scope.ServiceProvider.GetRequiredService<NautilusDbContext>();

                    Console.WriteLine("[IncomingEthTxService] Service is Running");

                    // Run something

                    await Task.Delay(5, stoppingToken);
                }
            }
        }
    }

如果你注意到了,那里有一个奖励。您必须使用服务范围才能访问数据库操作,因为它是单例的。

注入你的服务

// Background Service Dependencies
            services.AddSingleton<IHostedService, IncomingEthTxService>();

【讨论】:

  • 请将这些链接中的适用信息添加到您的答案中。 “仅链接”的答案在本网站上高度不鼓励,因为链接的页面可能会消失,从而使一个可能很好的答案变得毫无价值。
  • @BradleyUffner 现在好点了吗?
  • 很多!谢谢。
  • @BradleyUffner ;) 乐于分享
  • Dispose 会取消,这对我来说似乎有点奇怪。这意味着在 dispose 之后,取消流程仍然有可能使用来自刚刚处置的对象的数据。
猜你喜欢
  • 1970-01-01
  • 2011-01-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-23
相关资源
最近更新 更多