Search code examples
c#multithreadingasp.net-corerazor-pages

c# razor app, multithreading issue with "Cannot access a disposed object. A common cause of this error is disposing a context" issue


This is a .NET Razor app. I have an AJAX call in the CSHTML to a onpost method. The OnPost does some DB stuff, then calls a method that I would like to run in its own thread. If I await the method things work, but I need it to go off and do its thing and let user continue on form. It alwaus fails at the _context2.Attach(mo).State EntityState.Modified; with error Cannot access a disposed object. A common cause of this error is disposing a context. I have tried task/thread, changing return type to IactionResult etc.. always same issue. Sorry new to threading in C#. Any Ideas

public async Task<JsonResult> OnPostSignup(MarketOperator MarketOperator, Boolean Confirm = false)
{
Does some DB stuff
_context.Attach(RAExists).State = EntityState.Modified;
await _context.SaveChangesAsync();
   //Task.Run(async () =>  AI_Creation(_context,MarketOperator.MarketDescription, 
   //MarketOperator.MOID));

   Thread t3 = new Thread(() => AI_Creation(_context, 
   MarketOperator.MarketDescription, MarketOperator.MOID));
   t3.Start();

}

public async Task<IActionResult> AI_Creation(AppDbContext context,string idea, int moid)
{
//calls a bunch of open-ai calls which are awaited..  These work
enter code here
AppDbContext _context2 = context;
MarketOperator mo = await _context2.MarketOperator.FirstOrDefaultAsync(ra => ra.MOID == moid);
mo.Keywords = aI_Answers.keywords;

 _context2.Attach(mo).State = EntityState.Modified;
 await _context2.SaveChangesAsync()

}

Solution

  • That's because EF context is registered as scoped service by default.

    What you can do is however, is to create own scope - for that you need to inject IServiceProvider to your controller.

    So you define

    private readonly IServiceProvider serviceProvider;
    

    and assign it in contructor.

    Then first thing (before serviceProvider gets disposed) in method you need to create scope:

    public async Task<IActionResult> AI_Creation(string idea, int moid)
    {
        using var scope = serviceProvider.CreateScope();
        // you can use resolve context later on like this
        using var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();