问题描述
我正在使用带有ASP.NET Core 2.0的C#的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 demonstrates 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中的控制器操作运行后台任务的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!