I have a loop and within the loop I'm doing:
await Task.Delay(1000, ct).ContinueWith(async _ =>
{
await SecondMethodAsync(ct);
});
The second method gets an entity using EF, sets some properties and saves the entity back to the datastore by calling await SaveChangesAsync()
on the DbContext
.
The above should wait for 1s and then continue with the second method. With the above implementation I get the following exception:
A second operation started on this context before a previous asynchronous operation completed. Use 'await' to ensure that any asynchronous operations have completed before calling another method on this context. Any instance members are not guaranteed to be thread safe.
When I change the above to:
await Task.Delay(1000, ct);
await SecondMethodAsync(ct);
Everything works fine.
What is the difference with the above 2 snippets and how do I get to make the first snippet work?
CodePudding user response:
I think the reason is the following. Please correct me anybody if I am wrong.
The async
lambda inside ContinueWith
returns Task
, which means the ContinueWith
is actually done right after it gets that Task
(not when the Task
is completed). The program flow goes right into the next loop iteration and hitting that DBContext
again.
So the following might also work since the return type of ContinueWith
is Task<Task>
:
await await Task.Delay(1000, ct).ContinueWith(async _ => {
await SecondMethodAsync(ct);
});
CodePudding user response:
From A Tour of Task, Part 7: Continuations by the async/await guru Stephen Cleary:
In conclusion, I do not recommend using ContinueWith at all, unless you are doing dynamic task parallelism (which is extremely rare). In modern code, you should almost always use await instead of ContinueWith. There are several benefits to await.
You know the solution and it's great:
await Task.Delay(TimeSpan.FromSeconds(1), ct);
await SecondMethodAsync(ct);
CodePudding user response:
I think your problem is because of Entity framework, as u mentioned in SecondMethodAsync
you are using EF to do some stuff when you use ContinueWith
the inner method will continue in another thread parallel to main, and the caller thread wont block and wait for it.
but when you changed it to:
await Task.Delay(1000, ct);
await SecondMethodAsync(ct);
the main thread actually waits for the second operation as well.
so if you use the EF context elsewhere in the code after ContinueWith
, there might be concurrent calls to DB(in SecondMethodAsync
and your main code simultaneously).
i suggest that you need to get a new instance of your dbContext inside the await SecondMethodAsync
, or make sure that every call to db context must end before another call.
---- edit----
Michalor answer is simpler and better