Search code examples
c#entity-frameworktransactionscope

Overriding SaveChangesAsync when enclosed inside TransactionScope?


I'm fairly new to EF transactions, this is the code that is used for saving

public bool Save(TbArea area, bool isNew, out string errMsg)
        {
            try
            {
                errMsg = string.Empty;
    using (var oScope = new System.Transactions.TransactionScope(TransactionScopeOption.Required, TimeSpan.FromSeconds(120)))
                    {
                        try
                        {
                            TbArea oEntity = oContext.TbArea.Where(a => a.AreaCode == area.AreaCode && a.CompanyId == MainClass.SystemCompanyId).FirstOrDefault();
                            if (oEntity != null)
                            {
                                if (isNew) { errMsg = Resources.ResSales.MsgRecordCodeDublicated; return false; }
                                oContext.TbArea.Attach(oEntity);
                                oContext.Entry(oEntity).CurrentValues.SetValues(area);
                            }
                            else
                            {
                                if (!isNew) { errMsg = Resources.ResSales.MsgRecordNotFoundInDB; return false; }
                                oContext.TbArea.Add(area);
                            }

                            oContext.SaveChangesAsync();
                            oScope.Complete();
                            return true;
                        }
                        catch (Exception ex)
                        {
                            oScope.Dispose();
                            errMsg = ex.Message;
                            return false;
                        }
                    }

I'm overriding SaveChangesAsync so that I can save the ChangeTracker.Entries into the database. this is a portion of the code:

    dbContext.AcceptAllChanges();
    logsSet.AddRange(audits);
    int result = 0;
    try
      {
         result = await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
                //scope.Complete(); there was a transaction that I commented out, I thought it might overlaps the original transaction!

          return result;
       }
     catch (Exception ex)
          {
             var m = ex.Message;
             return result;
          }

When I save an item, I get the error :

the transaction has aborted

when I remove the transaction scope the saving happens normally!


Solution

  • Your code is marking the transaction complete before the changes have finished saving:

    oContext.SaveChangesAsync();
    oScope.Complete();
    

    You need to use await:

    await oContext.SaveChangesAsync();
    oScope.Complete();
    

    If you're in a context where await can resume on a different thread, you'll probably also need to specify TransactionScopeAsyncFlowOption.Enabled.