web-dev-qa-db-fra.com

Exécuter une tâche en arrière-plan à partir d'une action de contrôleur dans asp.net core 2

Je développe une application Web avec un REST Api utilisant C # avec asp.net core 2.0

Ce que je veux réaliser, c'est lorsque le client envoie une demande à un point de terminaison. J'exécute une tâche en arrière-plan séparée du contexte de la demande du client, qui sera terminé si la tâche a démarré correctement.

Je sais qu'il existe HostedService, mais le problème est que HostedService démarre au démarrage du serveur. Autant que je sache, il n'existe aucun moyen de démarrer HostedService manuellement à partir d'un contrôleur.

Voici un code simple qui illustre la 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);
    }
}
15
Waxren

Vous pouvez toujours utiliser IHostedService comme base pour les tâches en arrière-plan en combinaison avec BlockingCollection.

Créez un wrapper pour BlockingCollection afin de pouvoir l'injecter en tant que singleton.

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

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

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

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

Ensuite, dans la mise en œuvre de IHostedService, "écoutez" les tâches et lorsque les tâches "arrivent", exécutez-le.
BlockingCollection arrêtera l'exécution si la collection est vide - votre boucle while ne consommera donc pas de temps processeur.
.Take méthode accepte cancellationToken comme argument. Avec token, vous pouvez annuler l'attente de la tâche suivante lorsque l'application s'arrête.

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));
    }
}

Et dans le contrôleur, vous ajoutez simplement la tâche que vous souhaitez exécuter à notre 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();
    }
}

Le wrapper pour bloquer la collecte doit être enregistré pour l'injection de dépendance en tant que singleton

services.AddSingleton<TasksToRun, TasksToRun>();

Enregistrer le service d'arrière-plan

services.AddHostedService<BackgroundService>();
21
Fabio

Microsoft a documenté la même chose à l’adresse https://docs.Microsoft.com/en-us/aspnet/core/fundamentals/Host/hosted-services?view=aspnetcore-2.1

Il accomplit l'utilisation de BackgroundTaskQueue, qui obtient le travail assigné par Controller et le travail est effectué par QueueHostedService qui dérive de BackgroundService.

3
skjagini