2

Recently we upgraded to EF6 hopping to take advantage of its async call in our webapi controller but after some online reading i can to know

- EF6 async call is not thread safe 

While thread safety would make async more useful it is an orthogonal feature. It is  
unclear that we could ever implement support for it in the most general case, given that 
EF interacts with a graph composed of user code to maintain state and there aren't
easy ways to ensure that this code is also thread safe.

https://entityframework.codeplex.com/wikipage?title=Task-based%20Asynchronous%20Pattern%20support%20in%20EF.

but same thing is conveyed in this question EF Data Context - Async/Await & Multithreading

but when i looked at samples from MS http://msdn.microsoft.com/en-us/data/jj819165.aspx i am confused , because if i look at answers provided in stackoverflow question it seems currently we dont have any solution/pattern to implement it thread safe with single db context?

So my question is again how to achieve

var dbContext = new DbContext();
var something = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morething = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);

is a correct way with thread safe feature?

Community
  • 1
  • 1
Anshul Nigam
  • 1,608
  • 1
  • 12
  • 26
  • 2
    Why do you need it to be thread safe for the code you posted? – SoftwareFactor May 26 '14 at 06:42
  • think of code to be sitting inside a webapi controller, question on stackoverflow http://stackoverflow.com/questions/20946677/ef-data-context-async-await-multithreading also states the need to be thread safe – Anshul Nigam May 26 '14 at 06:46
  • "Thread safe" generally means "safe to use from multiple threads concurrently". The code you've got doesn't use the context in multiple threads at the same time, so thread safety seems like it should be a non-issue for you. –  May 26 '14 at 11:15

2 Answers2

2

From the same EF docs you quoted:

For the moment, EF will detect if the developer attempts to execute two async operations at one time and throw.

So, this code should work even if there's a thread switch after await, because it's still executed sequentially:

var dbContext = new DbContext();
var something = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morething = await dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);

At least, this is how it is expected to work. If sequential execution still produces a threading-related exception, this should be reported as an EF bug.

On the other hand, the following code will most likely fail, because we introduce parallelism:

var dbContext = new DbContext();
var somethingTask = dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 1);
var morethingTask = dbContext.someEntities.FirstOrDefaultAsync(e => e.Id == 2);

await Task.WhenAll(somethingTask, morethingTask);

var something = somethingTask.Result;
var morething = morethingTask.Result;

You need to make sure you don't use the same DbContext with more than one pending EF operation.

Updated, the 1st code fragment actually works fine with EF v6.1.0, as expected.

noseratio
  • 59,932
  • 34
  • 208
  • 486
  • I dont want EF to throw and error, and as mentioned in earlier stackoverflow question this will give you error "System.InvalidOperationException: The connection was not closed. The connection's current state is open." so seems it is not the perfect pattern/way to use. – Anshul Nigam May 26 '14 at 06:52
  • @AnshulNigam, we came to a [conclusion there](http://stackoverflow.com/a/20995146/1768303) that if you are seeing `InvalidOperationException` for the 1st code fragment (no parallelism for `DbContext`), you should report it as a bug with a minimal repro case: https://connect.microsoft.com/visualstudio. Make sure you're using the latest EF v6.1.0. – noseratio May 26 '14 at 06:57
  • @Noseration, in one of your answer to above question you had tried to solve it by creating your own schedular http://stackoverflow.com/questions/20993007/how-to-use-non-thread-safe-async-await-apis-and-patterns-with-asp-net-web-api so if you are saying that theoretically using await in non parallel environment is safe with EF6.1 and above then is it safe to assume that we don't need to have our own scheduler if we are using EF6.1? – Anshul Nigam May 26 '14 at 08:41
  • 1
    @AnshulNigam, that task scheduler was proposed as possible generic solution for objects requiring thread affinity. It might help with earlier EF versions, but apparently no longer needed with EF v6.1.0. You don't need thread affinity, you only need to eliminate parallelism for the same `DbContext`. – noseratio May 27 '14 at 02:14
  • @Noseration, thanks for your help, do we need to use it with ConfigureAwait(false) option? If yes why if no why. – Anshul Nigam May 27 '14 at 07:15
  • @AnshulNigam, no you don't need to call it `ConfigureAwait(false)` in ASP.NET. You want to stay on the same synchronization context (`AspNetSynchronizationContext`) while processing the HTTP request. Note it doesn't meant you'll stay on the same thread. – noseratio May 27 '14 at 07:36
1

The thing about Entity Framework contexts (and other ORMs for that matter) is that they are not thread safe. What this means it that you shouldn't share the same context object between more than one thread or you could face some of the following problems:

  • Entities not committed in the database would be shared between more than one execution plan;
  • Exceptions thrown by the context, invalidating every thread that is using that context, and how would you take care of that;
  • Memory leaks that could happen by not knowing when to dispose the context (is anyone still using the context?);

These are just a few subset of problems you would face by sharing the context. In fact, you should see the context as a unit of work for your current execution plan, that will store the entities and changes only relevant to you. After you complete everything, it should be disposed. Take a look at the following snippet:

using(var ctx = new MyDbContext()){
    var car = await ctx.Cars.FindAsync(id);
    car.Owner = new Person{ Name = "John Doe" };
    await ctx.SaveAsync();
}

Why do you need async method executions then? Because the thread currently executing, instead of waiting idle for the result of your database query, will be able to execute other jobs, like taking care of pending web requests, continue the execution of tasks that eventually terminated, etc. Note that every thread has an overhead and your thread pool can't use threads that are idle waiting for an operation to terminate.

What you should try to do, and since you are implementing a Web Application, is using a context by web request, because that's your execution plan. Maybe this can lead you further (Repository and UoW pattern with service layer). Normally I use IoC frameworks with factory patterns and interceptors, but if you want to keep it simple, you may create a wrapper for the HttpContext.Items and disposing your context at the end of the request.

Community
  • 1
  • 1
João Simões
  • 1,351
  • 1
  • 10
  • 20