ASP.NET Core IHostedService手动启动/停止/暂停(?)

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

我想在 ASPNET Core 中实现一个可以按需停止和启动的重复(定时)IHostedService 实例。 我的理解是 IHostedService 是由框架在应用程序启动时启动的。

但是,我希望能够“手动”启动/停止服务,也许可以通过 UI 使用开/关切换。 理想情况下,“关闭”状态将处理当前正在运行的服务,然后“打开”状态将创建一个新实例。

我在这里阅读了 MS 文档:https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-2.1.

我最初的想法是获取正在运行的服务的实例,然后调用公共

StopAsync(CancellationToken token)
方法。 然而,当涉及到应该传入哪个令牌时,我有点卡住了,对于
StartAsync(CancellationToken cancellationToken)
方法也是如此。

关于如何做到这一点,或者是否建议这样做,有什么想法吗? 我的方法是否在某种程度上违背了 ASPNET Core 中托管服务的预期设计?

编辑2018年7月27日

因此,经过更多研究(实际上是阅读文档:D),托管服务 StartAsync/StopAsync 方法确实意味着与应用程序的生命周期一致。 注册的IHostedServices似乎没有添加到DI容器中以注入到其他类中。

因此我认为我最初的想法行不通。 现在,我使用可以在运行时更新的配置依赖项 (

IOptions<T>
) 注册了我的服务。 当托管服务正在处理时,它将检查配置以查看是否应该继续,否则它将只是等待(而不是停止或处置托管服务)。

我可能很快就会将此标记为我的答案,除非我听到其他一些想法。

c# asp.net-core dependency-injection background-process
3个回答
25
投票

对于

StopAsync(CancellationToken token)
,您可以通过
new System.Threading.CancellationToken()
。在
public CancellationToken(bool canceled)
的定义中,
canceled
表示令牌的状态。对于您的场景,无需指定
canceled
,因为您想停止服务。

您可以按照以下步骤逐步操作:

  1. 创建

    IHostedService

       public class RecureHostedService : IHostedService, IDisposable
     {
    private readonly ILogger _log;
    private Timer _timer;
    public RecureHostedService(ILogger<RecureHostedService> log)
    {
        _log = log;
    }
    
    public void Dispose()
    {
        _timer.Dispose();
    }
    
    public Task StartAsync(CancellationToken cancellationToken)
    {
        _log.LogInformation("RecureHostedService is Starting");
        _timer = new Timer(DoWork,null,TimeSpan.Zero, TimeSpan.FromSeconds(5));
        return Task.CompletedTask;
    }
    
    public Task StopAsync(CancellationToken cancellationToken)
    {
        _log.LogInformation("RecureHostedService is Stopping");
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }
    private void DoWork(object state)
    {
        _log.LogInformation("Timed Background Service is working.");
    }
    }
    
  2. 注册

    IHostedService

        services.AddSingleton<IHostedService, RecureHostedService>();
    
  3. 启动和停止服务

     public class HomeController : Controller {
     private readonly RecureHostedService _recureHostedService;
     public HomeController(IHostedService hostedService)
     {
         _recureHostedService = hostedService as RecureHostedService;
     }
     public IActionResult About()
     {
         ViewData["Message"] = "Your application description page.";
         _recureHostedService.StopAsync(new System.Threading.CancellationToken());
         return View();
     }
    
     public IActionResult Contact()
     {
         ViewData["Message"] = "Your contact page.";
         _recureHostedService.StartAsync(new System.Threading.CancellationToken());
         return View();
     } }
    

5
投票

使用 Blazor Server,您可以通过以下方式启动和停止后台服务。 Asp.net Core MVC 或者 Razor 都是一样的原理

首先,实现一个IHostService

public class BackService : IHostedService, IDisposable
{
    private readonly ILogger _log;
    private Timer _timer;
    public bool isRunning { get; set; }
    public BackService(ILogger<V2rayFlowBackService> log)
    {
        _log = log;
    }

    public void Dispose()
    {
        _timer.Dispose();
    }

    public Task StartAsync(CancellationToken cancellationToken)
    {
        _log.LogInformation($"begin {DateTime.Now}");
        _timer = new Timer(DoWorkAsync, null, TimeSpan.Zero, TimeSpan.FromSeconds(10));
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        isRunning = false;
        _log.LogInformation($"{DateTime.Now} BackService is Stopping");
        _timer?.Change(Timeout.Infinite, 0);
        return Task.CompletedTask;
    }
    private void DoWorkAsync(object state)
    {
        _log.LogInformation($"Timed Background Service is working.  {DateTime.Now}");
        try
        {
            isRunning = true;
            // dosometing you want
        }
        catch (Exception ex)
        {
            isRunning = false;
            _log.LogInformation("Error {0}", ex.Message);
            throw ex;

        }
    }
}

Startup.cs中的注册服务

public void ConfigureServices(IServiceCollection services)
{
    services.AddSingleton<BackService>();
    services.AddHostedService(sp => sp.GetRequiredService<BackService>());
}

将后台服务注入 Blazor 组件

public class IndexBase:ComponentBase
{
    [Inject]
    BackService BackService { set; get; }

    protected override void OnInitialized()
    {
        if (BackService.isRunning)
        {
            BackService.StopAsync(new System.Threading.CancellationToken());
        }
        base.OnInitialized();
    }
    public void on()
    {
        if (!BackService.isRunning)
        {
            BackService.StartAsync(new System.Threading.CancellationToken());
        }

    }
    public void off()
    {
        if (BackService.isRunning)
        {
            BackService.StopAsync(new System.Threading.CancellationToken());
        }

    }
}
@page "/"
@inherits IndexBase
<h1>Hello, world!</h1>

Welcome to your new app.

<button @onclick="on">Start</button>
<button @onclick="off">Stop</button>

参考


4
投票

您应该从您自己的接口继承您的 HostedService 并将您的服务注册为 Singleton,但以不同的方式:

首先使用 AddHostedService 通用方法注册您的服务。

        services.AddHostedService<TimerHostedService>();

然后将一个公共静态字段添加到名为Instance的类中,该字段保存类的实例引用并在构造函数中设置其值!

然后在ConfigureServices中放置一个工厂,用于将您的服务注册为返回静态实例字段的单例!

示例代码如下: (在您的 HostedService.cs:

public interface ITimerHostedService : IHostedService
{

}

public class TimerHostedService : ITimerHostedService
{
    private static TimerHostedService _instance;

    public static TimerHostedService Instance => _instance;

    public TimerHostedService(ILogger<TimerHostedService> logger)
    {
        if(_instance == null)
        {
            _instance = this;
        }
    }
}

这是将您的服务注册为单例的代码(在 Startup.cs 中):

        services.AddHostedService<TimerHostedService>();

        services.AddSingleton<ITimerHostedService, TimerHostedService>(serviceProvider =>
        {
            return TimerHostedService.Instance;
        });

这是您的 Controller 中用于手动启动/停止 HostedService 的代码:

public class HomeController : Controller
{
    private readonly ILogger<HomeController> _logger;
    private readonly ITimerHostedService _hostedService;

    public HomeController(ILogger<HomeController> logger, ITimerHostedService hostedService)
    {
        _logger = logger;
        _hostedService = hostedService;
    }

    public async Task<IActionResult> Start()
    {
        await _hostedService.StartAsync(default);
        
        return Ok();
    }

    public async Task<IActionResult> Stop()
    {
        await _hostedService.StopAsync(default);
        
        return Ok();
    }
}

快乐编码!

享受你的美好时刻:X

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