Search code examples
c#.netentity-frameworkef-core-3.1

Is it ok to inject DbContext as singleton when there is only read operations?


I understand that DbContext is not thread-safe, plus DbContext caches data and it may leads to data inconsistency when several transactions try to save/commit their own changes into database. Thus, it is highly recommended to inject it per request (here). But I have a situation where only read operations exists (in a stand-alone class library) and there is no transaction or create/update/delete operations.
My question is: Is it safe to inject DbContext as singleton in this situation?


Solution

  • Entity Framework developers explicitly say that DbContext is not thread safe for any operations performed on it, not just write (add, save changes etc) operations, and you should just believe them on that if you don't want to spend days debugging mysterious failures one day.

    Even on read operations, EF can perform in-memory write operations on it's internal structures which are not thread safe, you cannot be sure it doesn't do that in any given case. For example, from documentation taking about processing of result set returned by a query:

    If the query is a tracking query, EF checks if the data represents an entity already in the change tracker for the context instance

    So if query is tracking query - it checks change tracker for current instance for already existing entity of this type with same key, which means if such entity doesn't exist - it puts it into change tracker. This is write operation, so not safe.

    You can say, well, I'll just use AsNoTracking() then. But here is another issue, about conncurrent AsNoTracking queries - EF won't even allow you to execute them anyway. Library maintainer says:

    Concurrent use of the same DbContext is not possible - and not just for tracked queries. Specifically, a DbContext has an underlying DbConnection to the database, which cannot be used concurrently. There are other various components at work under the hood which don't support multithreading.

    However, there's nothing wrong with instantiating several DbContexts and executing queries on them - whether tracking or non-tracking. That should get you the behavior you're looking for. If you run into any further issues don't hesitate to post back.

    So there are undocumented internal components in play which are not thread safe and you cannot be sure while doing anything on DbContext from multiple threads that you won't hit such components.