Search code examples
angularasp.net-core-webapibackgroundworker.net-core-angular

Polling from Angular via Core API and Background Service


I am trying to do polling from Angular to .NET Core API through BackgroundService.

I'll be passing inputs from the Angular component which calls the ApiController and that will trigger the BackgroundService and the outputs are updated back to the Angular Component in frequent intervals.

I've few queries.

  1. Can I achieve polling this way without any external libraries?
  2. How to pass parameters to the Worker ExecuteAsync?
  3. How to call the BackgroundService via GET API?

ApiController

[ApiController]
[Route("[controller]")]
public class TestController : ControllerBase
{
    private readonly ILogger<TestController> _logger;

    public TestController(ILogger<TestController> logger)
    {
        _logger = logger;
    }

    [HttpGet]
    public async Task<JsonResult> Get(CancellationToken cancellationToken)
    {
        var output = await Worker   //How to call the worker here and then return the output?
        return new JsonResult(output);
    }
}

Worker

public class Worker : BackgroundService
{
    private readonly ILogger<Worker> _logger;

    public Worker(ILogger<Worker> logger)
    {
        _logger = logger;
    }

    protected override async Task ExecuteAsync(CancellationToken stoppingToken)
    {
        while (!stoppingToken.IsCancellationRequested)
        {
            _logger.LogInformation("Worker running at: {time}", DateTimeOffset.Now);
            await Task.Delay(1000, stoppingToken);
            
           // Process inputs and return outputs in frequent intervals
        }
    }
}

Angular Component

import { Component, Inject } from '@angular/core';
import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';

@Component({
  selector: 'app-home',
  templateUrl: './home.component.html',
})
export class HomeComponent {

  public testForm: FormGroup;
  public test = new  FormControl('1');
  
  constructor(private fb: FormBuilder,
    http: HttpClient, @Inject('BASE_URL') baseUrl: string) {

    const result = interval(2000)
    .pipe(
      switchMap(() => http.get(baseUrl + 'test')),
      map(res => res))

    result.subscribe(res => {
       this.x.setValue(res);
    })
  }
}

Solution

  • There are many things here but I think You only interested in Calling worker.

    So first step.

    1. In ConfigureService services.AddScoped();

    2. In Controller constructor.

      private readonly ILogger _logger; private readonly Worker _worker; public TestController(ILogger logger,Worker worker) { _logger = logger; _worker = worker; }

       [HttpGet]
       public async Task<JsonResult> Get(CancellationToken cancellationToken)
       {
           var output = await ; //start worker
           return new JsonResult(output);
       }
      

    There are two approach to start worker. If you have inherited from Background service then call StartAsync. If you have implemented IHostedService then you have to do it your own. https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-3.1&tabs=visual-studio

    Note: I suspect that this approach will not give you update as you are awaiting for request to complete and before that result will not return.

    Update

    For Longer running background operation above approach will not work. For that you have to do some more work.

    1. When Angular Call Api.
    2. Create one work item in some table or store with unique id.
    3. Return that unique id to Angular.
    4. Background service will work from data available in store and it keep update status in store.
    5. Based on guid return to Angular from Api.
    6. You can call another api function to just check status of work item.

    Note: If you want to avoid polling then based on use case you can use SignalR or websocket.