0

I have two async functions.

  1. WriteActionToActionLog

    • documents action to log
    • add rows to db
    • async save
  2. SaveSetting

    • update rows in db.
    • pulls two rows by async list
    • update them by simple logic
    • save with async.

Both are async. Wrapped with transaction. Adding both tasks to task array. Using whenAll.

To reduce amount of code I will write /*logic*/.

Meanwhile I wrote it synced, since the delay is minor, but is seems to me way more correct using parallel tasks.

tried to use waitAll instead of whenAll. but answer did not return. tried to convert the async list within the SaveSetting function to synced. got the same error

.NET Framework is 4.6.1

public async Task<myObject> wrapperFunc(InnerServiceSettings clientSettings) {
   DbModel DB = new DbModel();  

   using (var transaction = DB.Database.BeginTransaction())
   {
      try
      {
         var tasks = new List<Task>();    

         /*logic - typeActionList created */     

         var settingsTask =  SettingsManager.SaveSetting(DB, clientSettings);
         tasks.Add(settingsTask);

         var logTask = AdminManager.WriteActionToActionLog(DB, typeActionList, clientSettings.idNum);
         tasks.Add(logTask);

         Task[] taskArr = tasks.ToArray();
         await Task.WhenAll(logTask, settingsTask); //exception thrown

         transaction.Commit();
         /*logic*/
      }
      catch(exception ex) {... rollback etc...}
   }    
}

public async static Task<bool> SaveSetting(DbModel iDb, ClientSettings iSettings)
{
   //list to be updated at db. One pull from db instead of several pulls. Also tried to made its sync, leaving save async. 
   //will return two rows from DB  
   var objListToChange = await iDb.Settings.Where(x => … ).ToListAsync();

   /*logic*/
   if (..)
   {
      var rowAtoUpdate = objListToChange.Where(x => …).FirstOrDefault();
      rowAtoUpdate.return_day = (short)iSettings.return_dayA;
   }

   if (…)
   {
      var rowBtoUpdate = objListToChange.Where(…).FirstOrDefault();
      rowBtoUpdate.return_day = (short)iSettings.return_dayB;                
   }

   try
   {
      var x = await iDb.SaveChangesAsync();
      return true;
   }
   catch (Exception e)
   {
      throw e;
   }
}

public async static Task<bool> WriteActionToActionLog(DbModel iDb, List<Tuple<int, int>> iActionTypeValue, int iManagerIdNum)
{    
   var ActionListToAdd = new List<ManagersActionLog>();

   foreach(var tupElem in iActionTypeValue)
   {
      ActionListToAdd.Add(new ManagersActionLog
      {
         ActionType = tupElem.Item1,
         ActionValue = tupElem.Item2,
         LogDate = DateTime.Now,
         ManagerId = iManagerIdNum,
      });
   }


   try {                   
      iDb.ManagersActionLog.AddRange(ActionListToAdd);
      var ans =  await iDb.SaveChangesAsync();
      return true;
   }
   catch(Exception ex) {
      throw ex;
   }
}

Once whenAll is reached I am getting: A second operation started on this context before a previous asynchronous operation completed. Use await to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.

Sentry
  • 4,102
  • 2
  • 30
  • 38
Ronen009
  • 111
  • 2
  • 9
  • 1
    You cannot use `DbContext` concurrently like this. Either do these operation sequentially (perhaps with a single `SaveChanges` at the end) or create a new context for every operation. – DavidG Aug 14 '19 at 08:45
  • 1
    Side note: You should change the way you handle exceptions: https://stackoverflow.com/questions/881473/why-catch-and-rethrow-an-exception-in-c – Sentry Aug 14 '19 at 14:01

0 Answers0