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

K. *_*ins 16 c# dependency-injection background-process asp.net-core

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

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

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

我最初的想法是获取正在运行的服务的实例,然后调用public StopAsync(CancellationToken token)方法.然而,当涉及到我应该传递哪个令牌时,我有点卡住了,对于该StartAsync(CancellationToken cancellationToken)方法也可以这样说.

关于如何做到这一点的任何想法,或者它是否可取?我的方法是否违反了ASPNET Core中托管服务的预期设计?

编辑7.27.2018

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

因此,我认为我最初的想法不会起作用.现在我用配置依赖项(IOptions<T>)注册了我的服务,可以在运行时更新.当托管服务正在处理时,它将检查配置是否应该继续,否则它将等待(而不是停止或处置托管服务).

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

Tao*_*hou 10

因为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.");
    }
    }
    
    Run Code Online (Sandbox Code Playgroud)
  2. 寄存器 IHostedService

        services.AddSingleton<IHostedService, RecureHostedService>();
    
    Run Code Online (Sandbox Code Playgroud)
  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();
     } }
    
    Run Code Online (Sandbox Code Playgroud)

  • 关于如何防止“自动启动”的任何想法? (4认同)
  • 当您有多个服务时,如何处理HomeController(IHostedService hostedService)那一部分?如何指定要使用哪个? (4认同)
  • 我可以停止另一个 IHostedService 中的 IHostedService 吗?我的意思是自动,而不是手动。 (2认同)

小智 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;

        }
    }
}
Run Code Online (Sandbox Code Playgroud)

Startup.cs 中的注册服务

public void ConfigureServices(IServiceCollection services)
    {
        services.AddSingleton<BackService>();
        services.AddHostedService(sp => sp.GetRequiredService<BackService>());
    }
Run Code Online (Sandbox Code Playgroud)

将后台服务注入 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());
        }

    }
}
Run Code Online (Sandbox Code Playgroud)
@page "/"
@inherits IndexBase
<h1>Hello, world!</h1>

Welcome to your new app.

<button @onclick="on">Start</button>
<button @onclick="off">Stop</button>
Run Code Online (Sandbox Code Playgroud)

参考