.NET Core 2.2, VS 2019
The problem is, the UpdateDatabaseWithFile
is never getting executed? What am I doing wrong here? I tried to wrap Directory
with an await, but I can't, since it returns void. What is the proper way to do this?
Here is the code:
Directory
.GetFiles(@"C:\Temp")
.ToList()
.ForEach(async file =>
{
await this._dataService.UpdateDatabaseWithFile();
});
It is because ForEach<T>
just executes that async
functions, but didn't wait for their completion.
You can use Select
for projecting Task
s which has to be executed and then use Task.WhenAll()
to execute that functions and wait for their completion:
var tasks = Directory
.GetFiles(@"C:\Temp")
.Select(async file =>
{
await this._dataService.UpdateDatabaseWithFile();
});
await Task.WhenAll(tasks);
Or if you want to execute functions sequentally, then instead of Task.WhenAll()
you can use simple foreach
:
foreach (var file in Directory.GetFiles(@"C:\Temp"))
{
await this._dataService.UpdateDatabaseWithFile();
}
Additional explanation:
Here is the implementation of ForEach<T>
:
for(int i = 0 ; i < array.Length; i++) {
action(array[i]);
}
As you see it just executes that action but didn't wait for their completion. And actually, it can't. Action
returns nothing. But for waiting Task
has to be returned. So, for making ForEach
function useful in that situation, it has to take Func
which return Task
and then await inside for
iterator..
The method List.ForEach
doesn't understand async delegates, in other words it hasn't an overload that accepts a Func<Task<T>>
parameter. In these cases what happens is that the supplied async lambda is treated as async void
. Async voids are evil , because they can't be awaited, so their execution can't be coordinated with other parts of the program. They are similar to fire-and-forget tasks, with the added drawback of killing the process in case of an exception.
Void-returning async methods have a specific purpose: to make asynchronous event handlers possible.
You should pay attention to the signature of the method you are attempting to call with an async delegate as argument. Some methods like Task.Run
have specific overloads for async delegates, and handle them well. Other methods like the LINQ Select
are not designed with async await in mind, but at least they accept arguments of type Func
instead of Action
, and so the generated tasks are returned and can be collected and awaited properly. Methods like the List.ForEach
with Action
arguments should never be used with async delegates.
The most likely problem here is that .ForEach()
provides no way for you to actually wait for the operations to complete. It will just execute them and move on.
Eric Lippert has a good blog post on why the .ForEach()
method isn't all that great, and this is a perfect example of how a foreach
loop excels over .ForEach
- foreach
will allow you to actually await the operations.
Just use a loop, and your problems go away:
foreach (var file = Directory.GetFiles(@"C:\Temp"))
{
await this._dataService.UpdateDatabaseWithFile();
}
It should be noted that you don't seem to actually be using the file
variable anywhere, and that seems like a problem. Hopefully just a typo?
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.