Start steering users away from AddDbContext/OnConfiguring
- Dominant language
- C#
- Stars
- 14.8k
- Forks
- 3.4k
- PR merge metrics
- PR metrics pending
Description
tl;dr start moving users towards factory-based patterns for configuring DbContext, to stop relying on EF's internal service provider cache to resolve the right service provider & singleton resources. This is important for supporting Cosmos, PostgreSQL, and possibly other providers.
When users specify options via AddDbContext/OnConfiguring, the lambda they provide for setting up the options gets executed repeatedly, possibly yielding different options for different contexts. While this can be useful in some scenarios (e.g. specifying different connection strings for different tenants), it also creates significant issues. To properly support this, EF manages an internal cache keyed on the options, so that the same service provider can be used if (and only if) singleton options do not have different values. This in turn requires EF to always be able to compare the options when doing cache lookups; but providers have arbitrary option configuration which may not be comparable at all: Cosmos has CosmosClientOptions, EFCore.PG has ConfigureDataSource() (accepting a lambda to configure the data source), etc.
We've discussed this, an there seems to be general agreement that it would be best to start moving away from these problematic configuration mechanisms, and towards factory-based DbContext instantiation, where the options are calculated only once, and then used for all contexts.
* Currently, the DI AddPooledDbContextFactory *only* registers a factory (though unpooled AddDbContextFactory does register both), making it harder to use (controllers can't get injected with a DbContext directly - not good). [#35484](https://github.com/dotnet/efcore/issues/35484) tracks registering both DbContext and IDbContextFactory in DI - I think that should be a prerequisite to this, to ensure a good experience.
* I think this means we end up discouraging AddDbContext, and recommending users always use AddDbContextFactory; this is slightly odd as users don't necessarily want to be injected with AddDbContextFactory - we only want them to use a configuration mechanism which computes the options once. Maybe introducing a new method with a nicer name would be better (??).
* We could think about this as a docs/guidance-only transition, or something more; for example, we could start with a warning when AddDbContext/OnConfiguring are used, gradually transition to obsoleting them, and then possibly consider if we want to actually remove them.
* Once this is all done, we could optionally also disable the service provider cache when the context factory is used; the factory would directly "own" the service provider, and any singleton resources inside it.
* Users who **do** need to vary options per context (e.g. connection string per tenant) would ideally have some mechanism for managing multiple IDbContextFactories - one per tenant. A DI registry for this, mapping the tenant ID to an IDbContextFactory, could make this experience good.
* Note that this whole issue is essentially the same as the ADO.NET connection string mess, where users instantiate a DbConnection with a connection string, and the driver internally manages a cache of connection pools. DbDataSource was introduced to ADO.NET to solve this problem, and is analogous to IDbContextFactory (basically an explicit source of the objects - DbContext or DbConnection - removing the need for any sort of internal lookup/caching).
Note: some of the above was already previously discussed in #29597.
/cc @NinoFloris with whom I've discussed this in past.
Contributor guide
Assessment
This issue has not been assessed yet.