[.NET Core] 백그라운드에서 동작하는 서비스

ASP.NET Core 앱을 개발하면서 Controller를 통해 호출하는 것이 아닌 실행과 동시에 Service 동작이 필요했다. IHostedService 인터페이스를 구현하면 ASP.NET Core에서 백그라운드 서비스를 구현 가능하다.

1. IHostedService의 구현체 만들기

public abstract class BackgroundWorker : IHostedService, IDisposable
{
    private Task executeTask;
    private readonly CancellationTokenSource cancellationTokenSource =
                                                   new CancellationTokenSource();

    protected abstract Task ExecuteAsync(CancellationToken stoppingToken);

    public virtual void Dispose()
    {
        cancellationTokenSource.Cancel();
    }

    public virtual Task StartAsync(CancellationToken cancellationToken)
    {
        executeTask = ExecuteAsync(cancellationTokenSource.Token);

        if (executeTask.IsCompleted)
        {
            return executeTask;
        }

        return Task.CompletedTask;
    }

    public virtual async Task StopAsync(CancellationToken cancellationToken)
    {
        if (executeTask == null)
        {
            return;
        }

        try
        {
            cancellationTokenSource.Cancel();
        }
        finally
        {
            await Task.WhenAny(executeTask,
                Task.Delay(Timeout.Infinite, cancellationToken));
        }
    }
}

2. 구현체 상속받아 필요한 서비스 만들기

public class MyWorker : BackgroundWorker
{
    private readonly IServiceScopeFactory serviceScopeFactory;

    public MyWorker(IServiceScopeFactory serviceScopeFactory)
    {
        this.serviceScopeFactory = serviceScopeFactory;
    }

    protected override async Task ExecuteAsync(CancellationToken cancellationToken)
    {
        using (var scope = serviceScopeFactory.CreateScope())
        {
            var myService = scope.ServiceProvider.GetRequiredService<MyService>();
            await myService.DoSomethingTask();
        }

    }
}

3. 서비스 등록하기

    ....
    
    public void ConfigureServices(IServiceCollection services)
    {
        ....
        
        services.AddSingleton<MyService>();
        services.AddHostedService<MyWorker>();
        
        ....
    }
    
    ....