Configuration
The DbContext
lifetime
DbContext
lifetimeThe lifetime of a DbContext
begins when the instance is created and ends when the instance is disposed.
A DbContext
instance is designed to be used for a single unit-of-work. This means that the lifetime of a DbContext
instance is usually very short.
"A Unit of Work keeps track of everything you do during a business transaction that can affect the database. When you're done, it figures out everything that needs to be done to alter the database as a result of your work." - Martin Fowler
A typical unit-of-work when using Entity Framework Core (EF Core) involves:
Creation of a
DbContext
instanceTracking of entity instances by the context. Entities become tracked by
Being returned from a query
Being added or attached to the context
Changes are made to the tracked entities as needed to implement the business rule
SaveChanges or SaveChangesAsync is called. EF Core detects the changes made and writes them to the database.
The
DbContext
instance is disposed
It is very important to dispose the DbContext after use. This ensures both that any unmanaged resources are freed, and that any events or other hooks are unregistered so as to prevent memory leaks in case the instance remains referenced.
DbContext is not thread-safe. Do not share contexts between threads. Make sure to await all async calls before continuing to use the context instance.
An InvalidOperationException thrown by EF Core code can put the context into an unrecoverable state. Such exceptions indicate a program error and are not designed to be recovered from.
DbContextOptions
The starting point for all DbContext
configuration is DbContextOptionsBuilder
.
There are three ways to get this builder:
In
AddDbContext
and related methodsIn
OnConfiguring
Constructed explicitly with
new
Configuring the database provider
DbContextOptions
versus DbContextOptions<TContext>
DbContextOptions
versus DbContextOptions<TContext>
Most DbContext
subclasses that accept a DbContextOptions
should use the generic DbContextOptions<TContext>
variation.
This ensures that the correct options for the specific DbContext
subtype are resolved from dependency injection, even when multiple DbContext
subtypes are registered.
Your DbContext
does not need to be sealed, but sealing is best practice to do so for classes not designed to be inherited from.
However, if the DbContext
subtype is itself intended to be inherited from, then it should expose a protected constructor taking a non-generic DbContextOptions
.
This allows multiple concrete subclasses to call this base constructor using their different generic DbContextOptions<TContext>
instances.
Notice that this is exactly the same pattern as when inheriting from DbContext
directly. That is, the DbContext
constructor itself accepts a non-generic DbContextOptions
for this reason.
A DbContext
subclass intended to be both instantiated and inherited from should expose both forms of constructor.
Avoiding DbContext threading issues
Entity Framework Core does not support multiple parallel operations being run on the same DbContext
instance. This includes both parallel execution of async queries and any explicit concurrent use from multiple threads. Therefore, always await
async calls immediately, or use separate DbContext
instances for operations that execute in parallel.
When EF Core detects an attempt to use a DbContext
instance concurrently, the application will throw an InvalidOperationException
.
Always await EF Core asynchronous methods immediately.
Last updated