Thread safe Entity Framework 6

10,268

I think you are facing an architectural issue. What you describes is an application where UI directly uses EF objects, and it breaks the "separation of concerns" paradigm.

On my side, I use customs thread-safe caches on a Model layer, letting everything happens on the Model layer. I implemented thread-safety on my cache with the well-known AsyncLock.

DbContext objects, and every EF CRUD related operations have a very limited lifetime. Each CRUD Operation instantiate it's own DbContext, and returns Model Objects to the cache, then, contexts are garbage collected. My applications uses caches as an abstraction layer, and caches uses EF as a DB abstraction layer.

For example, exploring attached properties on Objects, is done by implementing custom methods on the Model layer, which takes the object Id as parameter, and returns a list of related objects to the cache. The UI asks the Cache, then the Cache asks EF, then once available, the call made to the cache returns objects to the UI. Simple as that.

EntityFramework is not designed to be thread safe, so there's no way to work with it in a multi-threaded way. (EF thread safety)

Instead of having parallel access to you DbContext, you have to build a Model layer which can be accessed in a multi-threaded way. And your model can make multiple parallel calls to you DB, but keep in mind that each call must instantiate and keep it's own DbContext. At the end of each call, the related DbContext must be disposed.

DbContext are really fast to instantiate, the only downside is the network latency. That's why a memory cache is a good idea.

Share:
10,268

Related videos on Youtube

Rabbi
Author by

Rabbi

Updated on June 21, 2022

Comments

  • Rabbi
    Rabbi over 1 year

    Just starting testing EF6 and its Async functions. Boy was I surprised when I realized that they are not thread safe. I kinda assumed that that was the point.

    I had my own Task based extension methods for years already, but what I was waiting for from EF was to make them thread safe.

    At least my Task based functions locked as to not interfere with each other. EF6 doesn't even go that far. But the main problem is something that my code shares with theirs. i.e. Try issuing an async query and then before it completes try accessing a navigation property (on a pre-loaded totally separate entity in the same context) which triggers lazy loading. This could be triggered either by the UI, or by other code outside of the immediate function, or by a dozen other scenarios.

    As far as I can tell. The only two shared (between entities) mutable resources in a dbContext are the connection and change tracking (caching). If we could add locking around those to functionalities then we would have a thread safe context.

    We could even do it in two stages. If we could implement a provider that locked the one centralized function used to query the database. Then any non tracked queries - either by returning non entity (anonymous) objects or by calling AsNoTracking() - would be thread safe, and would be safe to call with Async functions even when another thread might be asking for a lazy loaded object.

    Our scaleability would be no worse off then we are now that we have to use one context per thread, and even the Async functions are off the table if you try to skip even one await to introduce a bit of parallelism or are working in an evented system (like wpf) that might trigger once the awaited function returns with the task.

    So my question is. Has anyone implemented a provider like this. Or would anyone be willing to work with me on it?

    • Aron
      Aron about 10 years
      Its interesting that EF6 not only is thread unsafe, but is threaded. The point of EF6 and Async functions is NOT threading. In fact async await is not a threading framework. Its an async framework. You can write single threaded asynchronous applications, in fact its the only thing you can do in node.js.
    • Stephen Cleary
      Stephen Cleary about 10 years
      I believe the general guidance when using EF6 async is to disable lazy loading completely. Otherwise you have a mixture of asynchronous and blocking code, which will always have behavioral quirks.
    • Rabbi
      Rabbi about 10 years
      @Aron you've read or heard a lot of Microsoft's propaganda without actually understanding it. async await CAN be implemented using node.js style single threaded asynchronous handling. Sometimes when you use async await you are not actually multithreading but rather just waiting for an external system to call back into your code. However most of the time Tasks are actually implemented using Threads. Tasks which are what async await is built on, are an abstraction which can in fact be either implementation but is usually threading.
    • Rabbi
      Rabbi about 10 years
      @StephenCleary That may be the general guidance because of the current implementation. But if someone were to work on a provider which locked around database access. Then we would be able to use both together.
    • Stephen Cleary
      Stephen Cleary about 10 years
      @Rabbi: The point is that you wouldn't want to. Lazy loading will needlessly block a thread on an asynchronous operation. If you either Include the related entities or perform a separate (asynchronous) followup request, your code will be fully asynchronous.
    • Stephen Cleary
      Stephen Cleary about 10 years
      @Rabbi: Most of the time asynchronous Tasks are based on (logical) events, not threads. In particular, this is true in EF6.
    • Aron
      Aron about 10 years
      @rabbi I was trying to draw the distinction between async and concurrent. Yes I know that async await CAN be used to read. But the problem here isn't CPU bound but latency bound. Threading increases inefficiency. Callback based event handling increases efficiency. I am therefore surprised that the EF team did not marshal back to the calling thread each time.
    • Aron
      Aron about 10 years
      Threading in my honest opinion should only be used in two situations to increase CPU resources for (locally)CPU bound processes and a sub set of that, segregation of CPU time for performance sensitive processes. SQL calls aren't CPU bound!
    • Rabbi
      Rabbi about 10 years
      @Aron I am surprised as you are that the EF team used threading to implement their asyncrony, and not just logical events. @Stephen they did use threads which is why running synchronous code brings the system down instead of just delaying the return. At the end of the day, always using Include or imperative code is not always an option and forces you to forego some of the major features that EF gives you. While all that would be able to be accomplished if we just wrapped one singe function in a lock
    • Arwin
      Arwin over 9 years
      There's some overlap with the discussion here: stackoverflow.com/questions/20993007/… , where the idea that "they are not thread-safe". The discussion there seems to suggest they are, in which case this question is no longer really valid.
    • Casey
      Casey almost 9 years
      I think you should be using more dbcontexts.
    • Salma Bouzid
      Salma Bouzid over 8 years
      The only issue I have had is that I occasionally get a thread-switch after SaveChangesAsync() and I was storing some entities on a threadstatic collection in order to do some logging. The entities would then appear to be missing; I got around it by grabbing a local copy before calling SaveChangesAsync; so long as all related entities were eager loaded (or I only accessed value types), it worked fine.
  • Casey
    Casey almost 9 years
    If I had a nickel for every EF problem that could be solved by instantiating DbContexts more frequently...