From:http://www.cnblogs.com/xishuai/p/ef-dbcontext-thread-safe.html
First look at this section of the exception information:
A second operation started on this context before a previous asynchronous operation completed. The use of ' await ' to ensure, which any asynchronous operations has completed before calling another method on the this context. Any instance members is not guaranteed to be thread safe.
Do not be prompted by the use of ' await ' in the message, if you look at the code carefully, found that there is no problem, the above exception information, we are often encountered in the async/await operation, what does it mean? We break down:
- A second operation started on this context before a previous asynchronous operation completed. : In this context, the second operation begins before the last asynchronous operation completes. It may be a bit around, simply put, in the same context, an asynchronous operation is not completed, and another operation begins.
- The use of ' await ' to ensure, which any asynchronous operations has completed before calling another method on the this context. : In this context, use await to ensure that all asynchronous operations are completed before another method call.
- Any instance member is not a guaranteed to be thread safe.: All instance members cannot be guaranteed to be thread secure.
What is thread safety?
- Thread safety, when a function or library is called in a multithreaded environment, it can correctly handle the local variables of each thread and make the program function complete correctly. (From Wikipedia)
is DbContext thread-safe?
- The context is not a thread safe. You can still create a multithreaded application as long as an instance of the same entity class was not track Ed by multiple contexts at the same time. (from MSDN)
Let's parse this passage, first of all, DbContext is not thread-safe, that is, you can only create one DbContext instance object (under certain circumstances) in the current thread, and this object cannot be shared, what does the following sentence mean? Note that the keywords, non-tracked entity classes, can be created in multiple contexts at the same time in a multithreaded application, what does it mean to not be tracked? Entities that can be understood as unmodified are obtained through this code: context.Entry(entity).State
.
We know that DbContext is like a big data container, through which we can easily query and modify data, in a previous blog post, there is a section of the EF DbContext SaveChanges source code:
[DebuggerStepThrough]PublicVirtualIntSaveChanges (bool acceptallchangesonsuccess) {var entriestosave = Entries. Where (E = e.entitystate = = Entitystate.added | | e.entitystate = = Entitystate.modified | | e.entitystate = EntityState. Deleted). Select (E = E.preparetosave ()). ToList (); if (!entriestosave.any ()) {return 0;} try {var result = SaveChanges (entriestosave); if (acceptallchangesonsuccess) {acceptallchanges (entriestosave);} return result; catch {foreach (var entry in entriestosave) {entry. Autorollbacksidecars (); } throw;}
Before DbContext executes the acceptallchanges, it detects the change of the entity state, so the SaveChanges will correspond to the current context one by one, and if it is a synchronous method, all operations are waiting, which is no problem, but imagine if it is asynchronous multithreading , when a thread creates a DbContext object and then makes some entity state modifications, another thread does the same thing before the acceptallchanges is executed, although the first thread can SaveChanges succeed, but the second thread will definitely get an error. Because the entity state has already been applied by DbContext in another thread.
In multi-threaded calls, the ability to properly handle the local variables of each thread, so that the program functions properly completed, which is thread-safe, but obviously DbContext does not guarantee that it will be done correctly, so it is not thread-safe, MSDN, the saying: any public static members Of this type is thread safe. Any instance members is not guaranteed to be thread safe.
Let's do a test to test the code:
using (var context = new TestDbContext2()){ var clients = await context.Clients.ToListAsync(); var servers = await context.Servers.ToListAsync();}
The above code is what we often write, a DbContext may have a lot of operations, test results are not a problem, we will then modify the code:
using (var context = new TestDbContext2()){ var clients = context.Clients.ToListAsync(); var servers = context.Servers.ToListAsync(); await Task.WhenAll(clients, servers);}
Task.whenall means that all waiting asynchronous operations are executed at the same time, and after execution you will find that the error is reported at the beginning of the day. And every once in a while? We first analyze the above two pieces of code, what is different, is actually asynchronous, but the following simultaneous execution of asynchronous methods, but not absolutely at the same time, so will occasionally error, according to the initial analysis of the DbContext, and the above test, we understand: the same moment, A context can only execute an asynchronous method, the first one is also an error, but the probability is very very small, can be ignored, the second way we just increase the probability, but not absolute.
In another case, if the project is more complex, we will generally design a DbContext based unitofwork, and then at the beginning of the project, the IoC injection mapping type, such as the following code:
new UnityContainer();container.RegisterType<IUnitOfWork, UnitOfWork>(new PerResolveLifetimeManager());
In addition to the mapping type, we also manage the life cycle of the Unitofwork object, which means that each request is parsed, that is, Unitofwork is unique for each request, only for the current request. Why do you design this? On the one hand, in order to share the injection of Iunitofwork objects, such as application in a number of Repository operations, but now I think, there is also a benefit is to reduce the risk of thread safety errors, because previously said, multithreaded case, a thread created DbContext, then modifies the entity state, the other thread creates the DbContext at the same time before applying the change, and also modifies the entity state, when the first thread creates the DbContext app changes, and the second thread creates the DbContext Applying the changes will result in an error, so one solution is to reduce the creation of DbContext, for example, the above request creates only one DbContext.
Because DbContext is not thread-safe, we should pay attention to the following two points when using it in multithreaded applications:
- At the same time, a context can only execute one asynchronous method.
- Entity state changes, corresponding to a context, can not be modified by the context of the entity State, or to apply the entity status.
Asynchronous under the use of DbContext, I personally think, no matter how the code is written, or will be reported thread-safe errors, but this chance will be very small and small, the application may run for a few years, there will be no error, but the probability of error with the garbage code and high concurrency, will slowly increase.
Resources:
- Managing DbContext the right-6:an in-depth guide with Entity Framework
- Multi-async in Entity Framework 6?
- Entity Framework working with DbContext
- Entity Framework Async Issues context or query?
- is DbContext thread safe?
- Can the DataContext be statically initialized?
- EntityFramework usage Exploration (vii) Thread safety practices
EntityFramework DbContext Thread Safety