wake-up-neo.com

Führen Sie eine Hintergrundaufgabe über eine Controller-Aktion in asp.net core 2 aus

Ich entwickle eine Webanwendung mit einer REST Api unter Verwendung von C # mit asp.net core 2.0

Was ich erreichen möchte, ist, wenn der Client eine Anforderung an einen Endpunkt sendet. Ich führe eine Hintergrundaufgabe aus, die vom Clientanforderungskontext getrennt ist und beendet wird, wenn die Aufgabe erfolgreich gestartet wurde.

Ich weiß, dass es HostedService gibt, aber das Problem ist, dass HostedService beim Starten des Servers gestartet wird und es meines Wissens keine Möglichkeit gibt, HostedService manuell von einem Controller aus zu starten.

Hier ist ein einfacher Code, der die Frage demonstriert.

[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

Sie können weiterhin IHostedService als Basis für Hintergrundaufgaben in Kombination mit BlockingCollection verwenden.

Erstellen Sie einen Wrapper für BlockingCollection, damit Sie ihn als Singleton einfügen können.

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

Bei der Implementierung von IHostedService "lauschen" Sie den Aufgaben und wenn Aufgaben "ankommen", führen Sie sie aus.
BlockingCollection stoppt die Ausführung, wenn die Sammlung leer ist - Ihre while -Schleife verbraucht also keine Prozessorzeit.
.Take Methode akzeptiert cancellationToken als Argument. Mit dem Token können Sie das Warten auf die nächste Aufgabe abbrechen, wenn die Anwendung stoppt.

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

Und im Controller fügen Sie einfach die Aufgabe hinzu, die Sie unserer Sammlung hinzufügen möchten

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

Der Wrapper zum Blockieren der Erfassung sollte für die Abhängigkeitsinjektion als Singleton registriert werden

services.AddSingleton<TasksToRun, TasksToRun>();

Registrieren Sie den Hintergrunddienst

services.AddHostedService<BackgroundService>();
21
Fabio

Microsoft hat dasselbe unter https://docs.Microsoft.com/en-us/aspnet/core/fundamentals/Host/hosted-services?view=aspnetcore-2.1 dokumentiert

Hierfür wird BackgroundTaskQueue verwendet, das die vom Controller zugewiesene Arbeit erhält, und die Arbeit wird von QueueHostedService ausgeführt, der von BackgroundService abgeleitet ist.

3
skjagini