简体   繁体   中英

C# Creating Class Instances on App Startup with Instance-Specific Timers that access the DB after Timer Elapsed

Hello StackOverflow Community,

I am working on something that I am not sure how to proceed with after trying a few things. I also have looked around for help for a while and haven't found anything that addresses my specific issue. I can't really provide much code at this time because it is a private project, but would love if someone could point me in the right direction conceptually.

Context : I am building a C# web app that, on startup, must grab a list of objects from the database (I am using EFCore and SQL) and assign to each one a timer that immediately starts running. Once the timer for each instance has elapsed, the callback function must update a value in the database for that specific instance, and that change will be displayed to the end users. I am using an IHostedService (specifically the StartAsync() function) to gather all of the objects from the DB when the app starts to run, and am able to set the timers for each object instance.

Problem : I believe my issue lies with the scoping of the DBContext. I kept getting the following error:

Cannot access a disposed context instance. A common cause of this error is disposing a context instance that was resolved from dependency injection and then later trying to use the same context instance elsewhere in your application.
This may occur if you are calling 'Dispose' on the context instance, or wrapping it in a using statement.
If you are using dependency injection, you should let the dependency injection container take care of disposing context instances.
Object name: 'DBContext'.

when I had the "using" statement in the BackgroundService file where I am creating my ScopeFactory for my ScopedService. If I remove the using statement, then I get this error:

A second operation was started on this context instance before a previous operation completed.
This is usually caused by different threads concurrently using the same instance of DbContext.
For more information on how to avoid threading issues with DbContext, see https://go.microsoft.com/fwlink/?linkid=2097913.

Question : Is there a way that I can instantiate the timer for each class instance on app startup that will allow me to access the DBContext within the Timer.Elapsed callback function?

I am hoping that I am just missing something small here, but if more detail is needed and someone would like to see some of my code to help out, I can happily do some renaming of variables and such to be able to share the code.

I really appreciate any input with this!

Your problem is most likely that you are injecting DbContext in IHostedService , which is usually a Singleton while DbContext is Scoped . In order to be able to use the DbContext in the IHostedService , you should create a scope:

public class DummyHosted : IHostedService
{
    private readonly IServiceProvider _provider;


    public DummyHosted(IServiceProvider provider)
    {
        this._provider = provider;
    }

    public async Task StartAsync(CancellationToken cancellationToken)
    {
        await using AsyncServiceScope scope = _provider.CreateAsyncScope();
        var dbContext = scope.ServiceProvider.GetRequiredService<YourDbContext>();
        // use db context safely as Scoped
    }

    public Task StopAsync(CancellationToken cancellationToken)
    {
        throw new NotImplementedException();
    }
}

Whenever possible you should provide code that explains your problem, even dummy code (if the project is private), so the community can better help you:)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM