2

I have created a Background Service in my Server Solution

public class PurgeService : IHostedService, IDisposable
{
    private readonly IServiceProvider _provider;
    private Timer timer;
    public PurgeService(IServiceProvider serviceProvider)
    {
        using (IServiceScope scope = serviceProvider.CreateScope())
        {
            _provider = scope.ServiceProvider;
        }
    }

    public void Dispose()
    {
        timer?.Dispose();
    }

    public Task Purge(IServiceProvider serviceProvider)
    {
        var dbcontext = serviceProvider.GetRequiredService<ApplicationDBContext>();
        var setting = dbcontext.AppSet.First();
        double deletetime = setting.PurgeTimer *(1);
        DateTime deletedate = DateTime.Now.AddHours(deletetime);
        string deleteSQL = $"DELETE FROM Notifications WHERE CreatedDate > {deletedate}"
    }
    public Task StartAsync(CancellationToken cancellationToken)
    {
        timer = new Timer(x => Purge(_provider), null, TimeSpan.Zero, TimeSpan.FromSeconds(10));
        return Task.CompletedTask;
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        return Task.CompletedTask;
    }

}

And added it to the Startup.cs

services.AddHostedService<PurgeService>();

My goal is to have a background service that checks every 10 seconds if there are notifications older than allowed and deletes them.

But when I run the app an error occurs

"System.ObjectDisposedException: "Cannot access a disposed object."

How can I implement this correctly?

mason
  • 31,774
  • 10
  • 77
  • 121
T0bi
  • 261
  • 1
  • 4
  • 13
  • Thanks for recreating your question with the focus on ASP.NET Core rather than Blazor - much appreciated. The code is always very much appreciated. There is one detail you left out - which line of code is throwing the exception? And I have an unrelated comment - you should use parameterized queries rather than using string interpolation to form them. While in this case there's no SQL injection vulnerability, it's still creating a new query plan each time, it'd be more efficient if this was parameterized. – mason Sep 13 '21 at 22:47
  • [Microsoft's documentation](https://learn.microsoft.com/en-us/aspnet/core/fundamentals/host/hosted-services?view=aspnetcore-5.0&tabs=visual-studio) shows changing the timer when the StopAsync method is called. Have you tried doing that? – mason Sep 13 '21 at 22:50

2 Answers2

2

Your constructor seems to be establishing a scope, and immediately disposing the scope. Your provider is tied to that scope so gets nuked immediately in the constructor when Dispose() is called on the scope due to using.

Background services are usually singletons with a lifetime equivalent to the lifetime of the the application. It seems to me you should register your service as a singleton outside of the service class itself. Something like

public class PurgeService : IHostedService
{
   // no weird constructor taking in IServiceProvider
}

Also, using an IoC inside of a class is a bit of an antipattern. Inject a DB context factory instead of trying to resolve through a container inside your class.

Kit
  • 20,354
  • 4
  • 60
  • 103
-1

The problem was that the database table was empty, but the null exception threw a completely different exception.

T0bi
  • 261
  • 1
  • 4
  • 13
  • The null exception threw a completely different exception? What does that mean? And you never even showed executing a query against the database in the code. You just showed construction of a webpage. – mason Sep 14 '21 at 12:53