从asp.net core 2中的控制器操作运行后台任务 [英] Run a background task from a controller action in asp.net core 2

查看:98
本文介绍了从asp.net core 2中的控制器操作运行后台任务的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在使用带有C#和asp.net core 2.0的REST Api开发Web应用程序

I am developing a web application with a REST Api using C# with asp.net core 2.0

我想要实现的是,当客户端向端点发送请求时,我将运行一个与客户端请求上下文分离的后台任务,如果任务成功启动,该任务将结束.

What I want to achieve is when the client send a request to an endpoint I will run a background task separated from the client request context which will be ended if the task started successfully.

我知道有HostedService,但是问题是HostedService在服务器启动时启动,据我所知,没有办法从控制器手动启动HostedService.

I know there is HostedService but the problem is that the HostedService starts when the server starts, and as far as I know there is no way to start the HostedService manually from a controller.

这是一个演示问题的简单代码.

Here is a simple code that demonstrate the question.

[Authorize(AuthenticationSchemes = "UsersScheme")]
public class UsersController : Controller
{

    [HttpPost]
    public async Task<JsonResult> StartJob([FromForm] string UserId, [FromServices] IBackgroundJobService backgroundService) {

           //check user account
           (bool isStarted, string data) result = backgroundService.Start();

           return JsonResult(result);
    }
}

推荐答案

您仍然可以将IHostedService用作与BlockingCollection结合使用的后台任务的基础.

You still can use IHostedService as base for background tasks in combination with BlockingCollection.

BlockingCollection创建包装器,以便将其作为单例注入.

Create wrapper for BlockingCollection so you can inject it as singleton.

public class TasksToRun
{
    private readonly BlockingCollection<TaskSettings> _tasks;

    public TasksToRun() => _tasks = new BlockingCollection<TaskSettings>();

    public void Enqueue(TaskSettings settings) => _tasks.Add(settings);

    public TaskSettings Dequeue(CancellationToken token) => _tasks.Take(token);
}

然后在实现IHostedService的过程中监听"任务,并在任务到达"时执行它.
如果集合为空,则BlockingCollection将停止执行-因此您的while循环将不会消耗处理器时间.
.Take方法接受cancellationToken作为参数.使用令牌,您可以在应用程序停止时取消等待"下一个任务.

Then in implementation of IHostedService "listen" for tasks and when tasks "arrive" execute it.
BlockingCollection will stop execution if collection is empty - so your while loop will not consume processor time.
.Take method accept cancellationToken as argument. With token you can cancel "waiting" for next task when application stops.

public class BackgroundService : IHostedService
{
    private readonly TasksToRun _tasks;

    private CancellationTokenSource _tokenSource;

    private Task _currentTask;

    public BackgroundService(TasksToRun tasks) => _tasks = tasks;

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        _tokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken);
        while (cancellationToken.IsCancellationRequested == false)
        {
            try
            {
                var taskToRun = _tasks.Dequeue(_tokenSource.Token);

                // We need to save executable task, 
                // so we can gratefully wait for it's completion in Stop method
                _currentTask = ExecuteTask(taskToRun);               
                await _currentTask;
            }
            catch (OperationCanceledException)
            {
                // execution cancelled
            }
        }
    }

    public async Task StopAsync(CancellationToken cancellationToken)
    {
        _tokenSource.Cancel(); // cancel "waiting" for task in blocking collection

        if (_currentTask == null) return;

        // wait when _currentTask is complete
        await Task.WhenAny(_currentTask, Task.Delay(-1, cancellationToken));
    }
}

然后在控制器中,您只需将要运行的任务添加到我们的集合中

And in the controller you simply add task you want to run to our collection

public class JobController : Controller
{
    private readonly TasksToRun _tasks;

    public JobController(TasksToRun tasks) => _tasks = tasks;

    public IActionResult PostJob()
    {
        var settings = CreateTaskSettings();

        _tasks.Enqueue(settings);

        return Ok();
    }
}

用于阻止收集的包装器应注册为单例进行依赖项注入

Wrapper for blocking collection should be registered for dependency injection as singleton

services.AddSingleton<TasksToRun, TasksToRun>();

注册后台服务

services.AddHostedService<BackgroundService>();

这篇关于从asp.net core 2中的控制器操作运行后台任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆