locked
Context used twice and complains about not being thread safe only in production dev is fine RRS feed

  • Question

  • User-183185495 posted

    I am still getting an errror about to contexts on this routine even though I have used the context multuple times within this method the weird thing is this all works on dev as of course we dont have to relie on time being the factor in dev but once in production I get the warnings about two threads being run can someone please help me out all the get commands do use a context.

    public async Task SetupViewBags() {
                GetEnforcementCats();
                GetUsersList();
                GetAllEnforcmentTypes();
                GetAllEnforcmentItems();
                GetUserName();
                GetStaffMemebers();
                GetPrioritys();
                GetNotificationsCount();
                var tennantId = GetCurrentTennantId().Result;
    
                ViewBag.CasesCount =  _context.MISobject.Where(w => w.OIC_1 == tennantId.ToString() || w.OIC_2 == tennantId.ToString()).Where(w => w.isDeleted == false && w.isActive == true).Count();
    
                var list = _userManager.Users.Select(x => new SelectListItem() { Text = x.FirstName.ToUpperInvariant() + " " + x.LastName.ToUpperInvariant(), Value = x.Id.ToString() }).ToListAsync();
                ViewBag.Users = list;
    
    }
     public async Task<int> GetNotificationsCount() {
                var userId = await GetCurrentTennantId();
                //the not should show all the notifcations should only check the desnitation user id
                var notificationCount= await  _context.Notifications.Where(w => w.SharedTo == userId.ToString()).CountAsync();
    
                ViewBag.NotificationCount = notificationCount;
    
                return notificationCount;
            }

    I made my DBContext scoped as was recomended but still i have issues

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

    This is some futher logs using the trace method

    info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
          Request finished in 165.3482ms 200 application/javascript
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (1ms) [Parameters=[@__ToString_0='?' (Size = 450)], CommandType='Text', CommandTimeout='30']
          SELECT COUNT(*)
          FROM [MISobject] AS [m]
          WHERE (([m].[OIC_1] = @__ToString_0) OR ([m].[OIC_2] = @__ToString_0)) AND (([m].[isDeleted] = CAST(0 AS bit)) AND ([m].[isActive] = CAST(1 AS bit)))
    info: Microsoft.AspNetCore.Hosting.Diagnostics[2]
          Request finished in 163.0282ms 200 application/javascript
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
          SELECT [a].[FirstName], [a].[LastName], [a].[Id]
          FROM [AspNetUsers] AS [a]
    info: Microsoft.EntityFrameworkCore.Database.Command[20101]
          Executed DbCommand (1ms) [Parameters=[], CommandType='Text', CommandTimeout='30']
          SELECT [a].[Id], [a].[AccessFailedCount], [a].[ConcurrencyStamp], [a].[Email], [a].[EmailConfirmed], [a].[FirstName], [a].[LastName], [a].[LockoutEnabled], [a].[LockoutEnd], [a].[NormalizedEmail], [a].[NormalizedUserName], [a].[PasswordHash], [a].[PhoneNumber], [a].[PhoneNumberConfirmed], [a].[SecurityStamp], [a].[TennantId], [a].[TwoFactorEnabled], [a].[UserName]
          FROM [AspNetUsers] AS [a]
    fail: Microsoft.EntityFrameworkCore.Query[10100]
          An exception occurred while iterating over the results of a query for context type 'MISSystem.Dal.MISDBContext'.
          System.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.
             at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
             at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
    System.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.
       at Microsoft.EntityFrameworkCore.Internal.ConcurrencyDetector.EnterCriticalSection()
       at Microsoft.EntityFrameworkCore.Query.Internal.QueryingEnumerable`1.AsyncEnumerator.MoveNextAsync()
    info: Microsoft.AspNetCore.StaticFiles.StaticFileMiddleware[2]
          Sending file. Request path: '/font-awesome/js/all.js'. Physical path: 'D:\GitMaster\MIS\uno\MISSystem.WEB\MISSystem.Web\MISSystem.Web\wwwroot\font-awesome\js\all.js'
    info: Microsoft.AspNetCore.Hosting.Diagnostics[2]

    Friday, August 7, 2020 7:09 PM

All replies

  • User475983607 posted

    This is a duplicate thread.  Typically this exception is due to a missing async.  you're code is missing an await on the GetNotificationsCount() method.

    public async Task SetupViewBags() {
                GetEnforcementCats();
                GetUsersList();
                GetAllEnforcmentTypes();
                GetAllEnforcmentItems();
                GetUserName();
                GetStaffMemebers();
                GetPrioritys();
    var tennantId = await GetCurrentTennantId(); await GetNotificationsCount(tennantId); ViewBag.CasesCount = _context.MISobject.Where(w => w.OIC_1 == tennantId.ToString() || w.OIC_2 == tennantId.ToString()).Where(w => w.isDeleted == false && w.isActive == true).Count(); var list = _userManager.Users.Select(x => new SelectListItem() { Text = x.FirstName.ToUpperInvariant() + " " + x.LastName.ToUpperInvariant(), Value = x.Id.ToString() }).ToListAsync(); ViewBag.Users = list; }

    IMHO, the code is a bit sloppy.  When you invoke GetCurrentTennantId().Result; you block the main thread which defeats the purpose is designing an  async/await solution.  I recommend passing the Userid to GetNotificationsCount(int userId)  rather than calling GetCurrentTennantId() twice.

    Use a method overload if needed (untested code).

    public async Task<int> GetNotificationsCount() {
        var userId = await GetCurrentTennantId();
        return await GetNotificationsCount(userId);
    }
    
     public async Task<int> GetNotificationsCount(int userId) {
        //the not should show all the notifcations should only check the desnitation user id
        var notificationCount= await  _context.Notifications.Where(w => w.SharedTo == userId.ToString()).CountAsync();
    
        ViewBag.NotificationCount = notificationCount;
    
        return notificationCount;
    }

    Friday, August 7, 2020 8:07 PM