asp.net-core-mvcasp.net-core-8

An operation started on this context before a previous operation completed


I am getting an error while running my ASP.NET Core 8 MVC app.

These are my controller methods hit by Ajax calls by Kendo controls:

public List<Location> GetLocations()
{
    return _db.Locations.ToList();  
}

public List<Region> GetRegions()
{
    return _db.Regions.ToList();
}

Now as the MS documentation says, it's because of database operations happening at the same time.

When I change the code to the following, it works:

public async Task<List<Location>> GetRegions()
{
    return _db.Locations.ToList(); 
}

public async Task<List<Region>> GetRegions()
{
    return _db.Regions.ToList();
}

I don't understand how this works. The same code can also hit database at the same time.

My only theory is that, Framework calls them

await GetLocation();
await GetRegion();

Or the framework just holds the new async calls until the executing ones are completed.

But that defeats the purpose of async.

Please let me know.

Thanks in advance


Solution

  • I had a test within my .net 8 MVC app, I created a button click event handler which would send 2 ajax requests to call my controller action. These 2 requests will call 2 methods like what you shared but I didn't get your exception. Based on my log, I can also see that the 2 requests has 2 different session ids.

    private readonly ApplicationDbContext _context;
     private readonly ILogger<MoviesController> _logger;
    
     public MoviesController(ApplicationDbContext context, ILogger<MoviesController> logger)
     {
         _context = context;
         _logger = logger;
     }
    
    public string test1() {
        var sessionId = HttpContext.Session.Id;
        _logger.LogError("sessionId is =====================: "+sessionId);
        GetMovie();
        return "success1";
    }
    
    public string test2()
    {
        var sessionId = HttpContext.Session.Id;
        _logger.LogError("sessionId2 is =====================: " + sessionId);
        GetMovieDesc();
        return "success2";
    }
    
    public List<Movie> GetMovie()
    {
        return _context.Movie.ToList();
    }
    
    public List<MovieDesc> GetMovieDesc()
    {
        return _context.MovieDesc.ToList();
    }
    

    Based on this section, we know that we should avoid using the same DbContext instance to run multiple parallel operations as DbContext is not thread safe. Therefore I deduce you are using the same _db instance to get your data from database. To resolve the issue, following the document:

    always await async calls immediately, or use separate DbContext instances for operations that execute in parallel

    The reason why your code could work only change the response type to asyn Task without changing to use await _db.Locations.ToListAsync(); shall relate to how .Net core handle tasks, if we don't add await but only return Task, your app will return the Task object immediately, and ASP.NET Core awaits it later --> you have await GetLocation(); which will wait for task completion as well. While both setting await and Task, it will wait for the task to complete inside the method, then return the result. I didn't find official MS document explained it, but I find this case. I trust it could help to understand.