36 votes

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

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

Ce que je veux atteindre, c'est quand le client envoie une requête à un point de terminaison, je vais exécuter en tâche de fond séparé de la demande du client un contexte qui sera terminé si la tâche a démarré avec succès.

Je sais qu'il est HostedService mais le problème est que le HostedService commence lorsque le serveur démarre, et autant que je sache, il n'y a aucune façon de commencer la HostedService manuellement à partir d'un contrôleur.

Voici un code simple qui démontrent 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);
    }
}

34voto

Fabio Points 2355

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

Créer un wrapper pour BlockingCollection de sorte que vous pouvez injecter en tant que 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);
}

Ensuite, dans la mise en œuvre de l' IHostedService "écouter" pour les tâches et lorsque les tâches "arrivée" de l'exécuter.
BlockingCollection va arrêter l'exécution si la collection est vide - de sorte que votre while la boucle ne sera pas consommer de temps processeur.
.Take méthode accepte cancellationToken comme argument. Avec un jeton, vous pouvez annuler "en attente" pour la prochaine tâche 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, il suffit d'ajouter 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();
    }
}

L'enveloppe pour le blocage de la collection doit être enregistrée pour l'injection de dépendance en tant que singleton

services.AddSingleton<TasksToRun, TasksToRun>();

Registre de service d'arrière-plan

services.AddHostedService<BackgroundService>();

11voto

skjagini Points 530

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

Il y parvient à l'aide de BackgroundTaskQueue, qui est assigné de Contrôleur et le travail est effectué par QueueHostedService qui dérive de BackgroundService.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X