Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why isn't SaveChangesAsync actually saving all of my changes?

I think I may be missing something in my understanding of how this is supposed to work. I have some code that imports a file. It loops through each record, does some processing, then adds that record to a table via a DbContext instance.

I initialize DbContext like this:

protected void ResetDbContext()
{
    if (_db != null)
        _db.Dispose();

    _db = new AppDBEntities();
    _db.Configuration.AutoDetectChangesEnabled = false;
    _db.Configuration.ValidateOnSaveEnabled = false;
}

My main loop looks something like this:

foreach (var rec in engine)
{
    var record = new CommonImportRecord(rec);
    ProcessRecord(record, options, index);
    index++;
}

_db.SaveChanges();

ProcessRecord looks something like this:

protected async Task<int> ProcessRecord(CommonImportRecord record, ImportOptions options, int index)
{
    DisplayProcessStatus(index, options);
    // Code removed which fills in various properties of the record
    _db.MyTable.Add(record);
    if (index % options.UpdateInterval == 0)
    {
        return await _db.SaveChangesAsync();
        // This was originally here, commented out when I changed SaveChanges() to SaveChangesAsync()
        // ResetDBContext();
    }
}

The only real changes I made for SaveChangesAsync() was to add async Task<int> as the return type of ProcessRecord, changed SaveChanges() to return await SaveChangesAsync() and commented out the call to ResetDBContext.

Things worked as expected before the async changes. Afterwards, it doesn't appear that all of my records are being saved.

What am I missing here?

like image 649
Paul Mrozowski Avatar asked Sep 23 '14 14:09

Paul Mrozowski


2 Answers

You are calling an async method which returns a task without waiting for it to complete. You need to use await to asynchronously wait before moving on to the next record. It's also a standard to name your async methods with the "Async" suffix:

foreach (var rec in engine)
{
    var record = new CommonImportRecord(rec);
    var result = await ProcessRecordAsync(record, options, index);
    index++;
}
like image 183
i3arnon Avatar answered Nov 08 '22 13:11

i3arnon


To add to @l3arnon's answer, you can save yourself the generating of a state machine inside ProcessRecordAsync:

This:

protected async Task<int> ProcessRecordAsync(CommonImportRecord record, ImportOptions options, int index)
{
    // Removed code for brevity 
    return await _db.SaveChangesAsync();
}

Can be turned into:

protected Task<int> ProcessRecordAsync(CommonImportRecord record, ImportOptions options, int index)
{
    // Removed code for brevity 
    return _db.SaveChangesAsync();
}

As you're not really using the return value of SaveChangesAsync inside the call to ProcessRecordAsync.

like image 4
Yuval Itzchakov Avatar answered Nov 08 '22 14:11

Yuval Itzchakov