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

.net async-await c# entity-framework-6 task-parallel-library

Question

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?

1
4
9/23/2014 3:14:45 PM

Accepted Answer

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++;
}
11
9/23/2014 2:08:33 PM

Popular Answer

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.



Related Questions





Related

Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow
Licensed under: CC-BY-SA with attribution
Not affiliated with Stack Overflow