0

I have method in which I want to execute 3 async methods. 2 of them (Method1 and Method3) uses dbContext to query data.

await Task.WhenAll(Method1(dbContext), Method2(), Method3(dbContext)).ConfigureAwait(false);;
await seedSession.SaveChangesAsync().ConfigureAwait(false);

With .ConfigureAwait(false); I get error

"A second operation started on this context before a previous asynchronous operation completed"

When it was without .ConfigureAwait(false); - everything worked fine.

Is problem in Method1 and Method3, which uses same context to make queries in same time?


Method1 just update data in db... Method2 - update data in blobs, Method3 - update data in azure table with sync to db... so Method1 and Method3 have "access" to db

demo
  • 6,038
  • 19
  • 75
  • 149
  • 1
    You can't use the same context to run queries in parallel. – DavidG Jan 21 '19 at 13:39
  • @DavidG, yes, it is logically... but why it works whitout `.ConfigureAwait(false)` ? for me it can be 50on50 that it will work – demo Jan 21 '19 at 13:42
  • What is `50on50`? – ColinM Jan 21 '19 at 13:44
  • @ColinM fifty-fifty – demo Jan 21 '19 at 13:45
  • With `ConfigureAwait(false)` you are (sort of) telling it to run in parallel. – DavidG Jan 21 '19 at 13:48
  • 1
    Does it really matter *why* it *apparently* doesn't uncover the same issues without `ConfigureAwait`? You're inappropriately sharing a single context object in a situation where you expect the methods sharing will execute simultaneously. You need to fix *that* no matter what the "why" of it may be. – Damien_The_Unbeliever Jan 21 '19 at 13:59
  • @demo you *can't* use the same *connection* to run queries in parallel. A context uses a single connection, as it should. If you want to execute queries in parallel a) **why**? parallelism won't fix data access bugs and b) you'll have to use *different* contexts/connections – Panagiotis Kanavos Jan 21 '19 at 14:58
  • @demo *why* do you want to run multiple methods at once? What are they doing? Why not use just one method that returns all results? Do you have a performance issue? Are you trying to load too much data or are there any missing indexes? Adding the correct index can result in 1000x speed improvement. – Panagiotis Kanavos Jan 21 '19 at 15:00
  • @PanagiotisKanavos, method1 - just update data in db... method2 - update data in blobs, method3 - update data in azure table with sync to db... – demo Jan 21 '19 at 15:09
  • @demo when you work with an ORM it's the ORM's job to make the updates. Just call `SaveChanges()` once and the ORM will send all updates in a batch. What you call `just` suggests there are multiple attempts to save *different* changes to the database. Never mind this exception. How do `Method1` and `Method3` know which records sould be updated by each one? – Panagiotis Kanavos Jan 21 '19 at 15:38
  • I find this question perfectly fine. I want to know the answer myself! It *does* matter *why* because we all want to learn und become better. – usr Jan 21 '19 at 15:39
  • @usr it could be a valid reason, it could be a bug [like this one](https://github.com/aspnet/EntityFrameworkCore/issues/12470) which doesn't involve async operations at all. Without the call stack and the EF version it's impossible to say what's wrong or even know where to look. Clearly EF and EF Core have critical sections that can only be called by a single thread. A single connection *doesn't* allow concurrent operations anyway. MARS allows *interleaving* of statements and results, it doesn't allow parallel operations. – Panagiotis Kanavos Jan 21 '19 at 16:19

1 Answers1

3

The answer to your question is quite simple, as many comments have suggested, you can't use a single context on two async methods that need to tun in parallel.

Regardless of whether you're using ConfigureAwait(false) you still have a fundamental design flaw that could blow up in your face at any time since two of the three methods share a context.

The reason you're seeing a 50/50 pass/fail rate is most likely dependant on how your methods are executed in the background. This may vary and sometimes Method1 might be done before Method3 needs to access the DB, then you don't see the error.

But again this is not the real issue here.

To get around this limitation you either have to run your methods synchronously (let them wait for each other) or you have to restructure your methods so that only one of them is responsible for DB access/updates. This is my suggestion to you.

Here's a good post that seems to suggest against the usage of ConfigureAwait(false), perhaps this is of interest to you. Best practice to call ConfigureAwait for all server-side code.

Hope this helps.