Say I have the following snippet:
using (var db = new dbContext()){
var user = db.Users.Find(3);
user.Name = "newName";
var viewModel = GetViewModelAndDoStuffToUser(user);
db.SaveChanges();
return viewmodel;
}
Now I'm trying to improve the performance of this snippet at scale by leveraging async/await, but I've read that dbContext is not "thread safe". So I'm a bit confused about:
async/await
with calls relating to dbContext.ConfigureAwait(false)
when I do. I've read that this tells us not to re-enter the ".NET Context" which is needed by MVC and WebApi controllers - but this is the service layer of an application serving those controllers. I've also read that using this will prevent deadlocks.dbContext
by using Task.WhenAll()
Would the following snippet be a scalable, thread-safe step in the right direction?
using (var db = new dbContext()){
var user = await db.Users.FindAsync(3).ConfigureAwait(false);
user.Name = "newName";
var viewModel = await GetViewModelAndDoStuffToUserAsync(user).ConfigureAwait(false);
await db.SaveChangesAsync().ConfigureAwait(false);
return viewmodel;
}
A situation to use ConfigureAwait(true) is when performing await in a lock, or using any other context/thread specific resources. This requires a synchronization context, which you will have to create, unless you are using Windows Forms or WPF, which automatically create a UI synchronization context.
When an asynchronous method awaits a Task directly, continuation usually occurs in the same thread that created the task, depending on the async context. This behavior can be costly in terms of performance and can result in a deadlock on the UI thread. To avoid these problems, call Task. ConfigureAwait(false) .
ConfigureAwait(continueOnCapturedContext: false) is used to avoid forcing the callback to be invoked on the original context or scheduler. This has a few benefits: Improving performance.
Short answer is "Yes", "Yes", and "Yes".
Long answer is that although db context is not thread-safe, async
/await
constructs are not passing db context to other threads anyway, so you are safe here.
As far as the call to ConfigureAwait(false)
goes, you should do it everywhere except UI code. Since database code is rarely placed in UI layer (it is never placed in UI level in high-quality production code) you should use ConfigureAwait(false)
in every asynchronous call that you make on backend.
Finally, if you make multiple tasks with calls to async
methods of db context, you create co-routines. They parallelize work outside your system (e.g. make concurrent DB calls), but on your end they are not running concurrently. This keeps your db context safe, because any changes to it are applied sequentially.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With