asp.NET Core定时BackgroundService无法通过StopAsync()在Controller中停止

问题描述 投票:0回答:4

我已经用asp.NET Core实现了REST API服务器。我需要执行一些管理工作的后台任务。

我尝试了网络中的一些建议以创建一个定时的后台任务,并将其实现为:BackgroundService.cs

public abstract class BackgroundService : IHostedService, IDisposable
{
    private Task _executingTask;
    private readonly CancellationTokenSource _stoppingCts = new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        _executingTask = ExecuteAsync(_stoppingCts.Token);

        if (_executingTask.IsCompleted)
        {
            return _executingTask;
        }

        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        if (_executingTask == null)
        {
            return;
        }

        try
        {
            _stoppingCts.Cancel();
        }
        finally
        {
            await Task.WhenAny(_executingTask, Task.Delay(Timeout.Infinite, cancellationToken));
        }

    }

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

RecureHostedService.cs

public class RecureHostedService : BackgroundService
{
    private static readonly ILog log = LogManager.GetLogger(MethodBase.GetCurrentMethod().DeclaringType);
    private int _executionCount = 0;
    private Timer _timer;

    protected async override Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            var count = Interlocked.Increment(ref _executionCount);

            log.InfoFormat(
                "Timed Hosted Service is working. Count: {0}", count);
            await Task.Delay(10000, stoppingToken);
        }
    }
}

SystemController.cs

public class SystemController : Controller
{
    private readonly RecureHostedService _recureHostedService;

    public SystemController(IHostedService hostedService)
    {
        _recureHostedService = hostedService as RecureHostedService;
    }
    [HttpGet(ApiRoutes.System.Start)]
    public IActionResult Start()
    {
        Console.WriteLine("Start Service");
        _recureHostedService.StartAsync(new CancellationToken());
        return Ok();
    }

    [HttpGet(ApiRoutes.System.Stop)]
    public IActionResult Stop()
    {
        Console.WriteLine("Stop Service");
        Console.WriteLine(_recureHostedService == null);
        _recureHostedService.StopAsync(new CancellationToken());
        return Ok();
    }
}

在Startup.cs中,我将该服务添加为Singleton:

services.AddSingleton<IHostedService,RecureHostedService>();

RecureHostedService在我的应用程序启动时开始工作。当我从SystemController执行APIrequest / api / v1 / stop(方法Stop())时,在

中得到了NullReferenceException
_recureHostedService.StopAsync(new CancellationToken());

我发现私有成员变量_recureHostedService为null,即使我已经在SystemController的构造函数中为其分配了它。因此,看来_recureHostedService的服务注入无效。所以这行

_recureHostedService = hostedService as RecureHostedService;

在SystemController的构造方法中,将_recureHostedService分配为null。怎么可能?我已经将许多其他服务注入其他控制器,并且一切正常。任何人的想法,为什么它不能与该托管服务一起使用?

最佳麦克风

c# asp.net-core background-service
4个回答
0
投票

您需要使用AddHostedServiceAddSingleton安装程序注册托管服务所以会是这样

services.AddHostedService<IHostedService,RecureHostedService>();

0
投票

如果您在构造函数上注入IHostedService,请保持声明为[[IHostedService类型的_recureHostedService属性,而不是具体的类RecureHostedService。这里没有必要向下转换。

[Route("[controller]")] public class SystemController : Controller { private readonly IHostedService _recureHostedService; public SystemController(IHostedService hostedService) { _recureHostedService = hostedService ?? throw new ArgumentNullException(nameof(hostedService)); } }

更新

我已经测试了该控制器,无论哪种方式都不再存在null异常

services.AddSingleton<IHostedService, RecureHostedService>();

services.AddHostedService(p => { return new RecureHostedService(); });


0
投票
使用依赖项注入获取IHostedService将注入首先定义的任何托管服务。这很可能是Web服务器本身。相反,您应该定义两个返回相同单例的服务定义;

services.AddSingleton<RecureHostedService>(); services.AddSingleton<IHostedService>(p => p.GetRequiredService<RecureHostedService>()); public class SystemController : Controller { private readonly RecureHostedService _recureHostedService; public SystemController(RecureHostedService hostedService) { _recureHostedService = hostedService; } }


0
投票
我为自己使用了一组静态变量

public class RecureHostedService : BackgroundService { public static bool isPause = false; /* for example public static string ErrorText; public static bool isError = false; public static bool isWorked = false; public static bool firstStart = true; */ protected async override Task ExecuteAsync(CancellationToken stoppingToken){ while (!stoppingToken.IsCancellationRequested) { await Task.Delay(TimeSpan.FromSeconds(1), stoppingToken); if (isPause) continue; var count = Interlocked.Increment(ref _executionCount); log.InfoFormat( "Timed Hosted Service is working. Count: {0}", count); await Task.Delay(10000, stoppingToken); } } } public class SystemController : Controller { [HttpGet(ApiRoutes.System.Start)] public IActionResult Start() { Console.WriteLine("Start Service"); RecureHostedService.isPause = false; return Ok(); } [HttpGet(ApiRoutes.System.Stop)] public IActionResult Stop() { Console.WriteLine("Stop Service"); RecureHostedService.isPause = true; return Ok(); } }

© www.soinside.com 2019 - 2024. All rights reserved.