List<T>.ForEach
doesn’t play particularly well with async
(neither does LINQ-to-objects, for the same reasons).
In this case, I recommend projecting each element into an asynchronous operation, and you can then (asynchronously) wait for them all to complete.
using (DataContext db = new DataLayer.DataContext())
{
var tasks = db.Groups.ToList().Select(i => GetAdminsFromGroupAsync(i.Gid));
var results = await Task.WhenAll(tasks);
}
The benefits of this approach over giving an async
delegate to ForEach
are:
- Error handling is more proper. Exceptions from
async void
cannot be caught withcatch
; this approach will propagate exceptions at theawait Task.WhenAll
line, allowing natural exception handling. - You know that the tasks are complete at the end of this method, since it does an
await Task.WhenAll
. If you useasync void
, you cannot easily tell when the operations have completed. - This approach has a natural syntax for retrieving the results.
GetAdminsFromGroupAsync
sounds like it’s an operation that produces a result (the admins), and such code is more natural if such operations can return their results rather than setting a value as a side effect.