0

I have a small issue i keep getting I am using view components on a page and I keep getting the following. I am using asp.net core 3.1 and ef core 3.1

A database operation failed while processing the request. InvalidOperationException: A second operation started on this context before a previous operation completed. This is usually caused by different threads 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. There are pending model changes for MISDBContext In Visual Studio, use the Package Manager Console to scaffold a new migration for these changes and apply them to the database:

PM> Add-Migration [migration name] PM> Update-Database Alternatively, you can scaffold a new migration and apply it from a command prompt at your project directory:

dotnet ef migrations add [migration name] dotnet ef database update

I have checked on SO and it recommended that you add the following to your class now I am i right in thinking i dont need the second context at all is that what is causing the issue in my case. I added the line

ServiceLifetime.Transient

Apparently the above line allows for more concurrent connections to the context.

services.AddDbContext<MISDBContext>
 (options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);

services.AddDbContext<ApplicationDbContext>(options =>
 options.UseSqlServer(
 Configuration.GetConnectionString("DefaultConnection")), ServiceLifetime.Transient);            

This is the typical layout of my view component I think in fact it may be this one that is causing the issue cause im trying to access the user store at the same time as the context.

 private Task<ApplicationUser> GetCurrentUserAsync() => _userManager.GetUserAsync(HttpContext.User);
    public async Task<Guid> GetCurrentTennantId() {
        ApplicationUser usr = await GetCurrentUserAsync();
        TempData["TeannantId"] = usr?.Id;
        Guid.TryParse(usr?.Id, out Guid resultTennantId);
        return resultTennantId;
    }
    private Task<List<ApplicationUser>> GetItemsAsync() {
        string currentUser =  GetCurrentTennantId().Result.ToString();
        var excludeCurrentUser= _userManager.Users.Where(w => w.Id != currentUser).ToListAsync();
        return excludeCurrentUser;
    }
}
c-sharp-and-swiftui-devni
  • 3,743
  • 4
  • 39
  • 100
  • 1
    Which DbContext is `_userManager` using? Are you using two dbContexts at the same time? Also, do you *really* need two dbContexts for the same database? Some interesting read [here](https://stackoverflow.com/questions/11197754/entity-framework-one-database-multiple-dbcontexts-is-this-a-bad-idea). – Prolog Jul 16 '20 at 21:01
  • Its using MISDBContext that is the problem i removed the offending one. – c-sharp-and-swiftui-devni Jul 16 '20 at 21:08
  • In my opinion, to solve this issue, you should make sure you have always await async calls immediately when you call GetCurrentUserAsync and GetCurrentTennantId method. – Brando Zhang Jul 17 '20 at 02:56

1 Answers1

1

Apparently the above line allows for more concurrent connections to the context.

No; it creates a new context for each request. Each context can only be used for one operation at a time. So, e.g., if your context was previously a singleton, then only one HTTP request could make an operation at a time.

I think in fact it may be this one that is causing the issue cause im trying to access the user store at the same time as the context.

This code by itself can't cause the problem, but if this pattern is repeated in your code base, then it could cause the problem.

In summary, use async and await for all Task<T>-returning methods, unless the method is trivial. E.g., GetCurrentUserAsync is trivial (it's just passing through to another method), so it's fine to elide async and await there. GetItemsAsync is definitely not trivial, so it should be using async and await:

private async Task<List<ApplicationUser>> GetItemsAsync() {
  string currentUser = (await GetCurrentTennantId()).ToString();
  return await _userManager.Users.Where(w => w.Id != currentUser).ToListAsync();
}

If you apply async and await in a similar manner across your codebase, you should resolve the concurrent context issue.

Stephen Cleary
  • 437,863
  • 77
  • 675
  • 810