I want to know what is best way to call nested async methods, if I want both methods to be async.
First: Caller method must just return Task
public async Task SaveChangesAsync()
{
await Context.SaveChangesAsync();
}
public Task UpdateAsync(List<TEntity> entities)
{
foreach (TEntity entity in entities)
{
BeforeUpdate(entity);
Context.Entry(entity).State = EntityState.Modified;
}
return SaveChangesAsync();
}
Second: Caller method must have an async-await
as called method
public async Task SaveChangesAsync()
{
await Context.SaveChangesAsync();
}
public async Task UpdateAsync(List<TEntity> entities)
{
foreach (TEntity entity in entities)
{
BeforeUpdate(entity);
Context.Entry(entity).State = EntityState.Modified;
}
await SaveChangesAsync();
}
This code should not use async/await
, unless there are pieces in those methods you have not shown.
In other words, this is how these methods should be written:
public Task SaveChangesAsync()
{
return Context.SaveChangesAsync();
}
public Task UpdateAsync(List<TEntity> entities)
{
foreach (TEntity entity in entities)
{
BeforeUpdate(entity);
Context.Entry(entity).State = EntityState.Modified;
}
return SaveChangesAsync();
}
This is basically option 3 which you did not show, no trace of async/await
at all.
The reason for this is that the way the code will execute is as follows:
UpdateAsync
leading up to the await, on the calling thread, which means the foreach
-loop will be executing on the calling threadSaveChangesAsync()
up until it returns, still on the calling threadSaveChangesAsync
will call into Context.SaveChangesAsync()
until it returns, still on the calling threadIn short, all the work of these methods have already been done on the calling thread, what is left is only the async-ness of Context.SaveChangesAsync
. If you drop async/await
completely and simply return the task that wrap that async-ness all the way back out, you have not sacrificed any functionality but you have removed all the complexity that the compiler generates for you with an async/await
method.
The general rule is this:
Unless you need to do something after the async method returns, don't use
async/await
Since both your methods ends right after the call to the async method, you're better off just returning the tasks involved instead and not using async/await
.
An await
can work on any Task
. It doesn't have to be the result of an async
method. In fact, it doesn't have to be the result of any method at all.
That said, I recommend that any methods with non-trivial logic in them should use async
/await
:
public async Task UpdateAsync(List<TEntity> entities)
{
foreach (TEntity entity in entities)
{
BeforeUpdate(entity);
Context.Entry(entity).State = EntityState.Modified;
}
await SaveChangesAsync();
}
This is because async
captures exceptions in the method body and places them on the returned Task
, which is expected behavior when calling task-returning methods. If you elide the async
/await
here, then any exceptions from BeforeUpdate
or setting State
would be thrown directly rather than placed on the Task
.
On the other hand, your calling method is trivial, so you can elide async
/await
there:
public Task SaveChangesAsync()
{
return Context.SaveChangesAsync();
}
When you go async, await
and async
need to be used through the call stack, otherwise the code will not be executed asynchronously. So use async
on both methods.
If an async method doesn’t use an await operator to mark a suspension point, the method executes as a synchronous method does, despite the async modifier
https://msdn.microsoft.com/en-us/library/mt674882.aspx
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