Skip to content

Commit 8cc1754

Browse files
Document ConfigureDbContext method and AddDbContext precedence changes in EF Core 8.0 (#5118)
Fixes #4588 Fixes #4923 --------- Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com>
1 parent 8b77430 commit 8cc1754

4 files changed

Lines changed: 212 additions & 6 deletions

File tree

entity-framework/core/dbcontext-configuration/index.md

Lines changed: 74 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -90,8 +90,6 @@ The final result is an `ApplicationDbContext` instance created for each request
9090

9191
Read further in this article to learn more about configuration options. See [Dependency injection in ASP.NET Core](/aspnet/core/fundamentals/dependency-injection) for more information.
9292

93-
<!-- See also [Using Dependency Injection](TODO) for advanced dependency injection configuration with EF Core. -->
94-
9593
## Basic DbContext initialization with 'new'
9694

9795
`DbContext` instances can be constructed with `new` in C#. Configuration can be performed by overriding the `OnConfiguring` method, or by passing options to the constructor. For example:
@@ -425,6 +423,80 @@ Any code that explicitly executes multiple threads in parallel should ensure tha
425423

426424
Using dependency injection, this can be achieved by either registering the context as scoped, and creating scopes (using `IServiceScopeFactory`) for each thread, or by registering the `DbContext` as transient (using the overload of `AddDbContext` which takes a `ServiceLifetime` parameter).
427425

426+
<a name="configuredbcontext"></a>
427+
428+
## ConfigureDbContext for configuration composition
429+
430+
> [!NOTE]
431+
> This section covers intermediate-level usage of EF Core primarily intended for reusable libraries and components. Most applications should use the `AddDbContextFactory` pattern described earlier in this article.
432+
433+
Starting with EF Core 9.0, you can use <xref:Microsoft.Extensions.DependencyInjection.EntityFrameworkServiceCollectionExtensions.ConfigureDbContext*> to apply additional configuration to a `DbContext` either before or after the `AddDbContext` call. This is particularly useful for composing non-conflicting configuration in reusable components or tests.
434+
435+
### Basic ConfigureDbContext usage
436+
437+
`ConfigureDbContext` allows you to add configuration in a reusable library or component without replacing the entire provider configuration:
438+
439+
<!--
440+
var services = new ServiceCollection();
441+
442+
services.ConfigureDbContext<BlogContext>(options =>
443+
options.EnableSensitiveDataLogging()
444+
.EnableDetailedErrors());
445+
446+
services.AddDbContext<BlogContext>(options =>
447+
options.UseInMemoryDatabase("BasicExample"));
448+
449+
var serviceProvider = services.BuildServiceProvider();
450+
-->
451+
[!code-csharp[BasicConfigureDbContext](../../../samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs?name=BasicConfigureDbContext)]
452+
453+
### Provider-specific configuration without connection strings
454+
455+
To apply provider-specific configuration you can use provider-specific configuration methods without supplying the connection string. The SQL Server provider also includes `ConfigureSqlEngine` for this case. See [SQL Server-specific batching behavior](xref:core/providers/sql-server/misc#configuresqlengine) for more information.
456+
457+
<!--
458+
var services = new ServiceCollection();
459+
460+
services.ConfigureDbContext<BlogContext>(options =>
461+
options.UseSqlServer(sqlOptions =>
462+
sqlOptions.EnableRetryOnFailure()));
463+
464+
services.AddDbContext<BlogContext>(options =>
465+
options.UseSqlServer("connectionString"));
466+
467+
var serviceProvider = services.BuildServiceProvider();
468+
-->
469+
[!code-csharp[ProviderSpecificConfiguration](../../../samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs?name=ProviderSpecificConfiguration)]
470+
471+
### ConfigureDbContext and AddDbContext precedence
472+
473+
When both `ConfigureDbContext` and `AddDbContext` are used, or when multiple calls to these methods are made, the configuration is applied in the order the methods are called, with later calls taking precedence for conflicting options.
474+
475+
For non-conflicting options (like adding logging, interceptors, or other settings), all configurations are composed together:
476+
477+
<!--
478+
var services = new ServiceCollection();
479+
480+
services.ConfigureDbContext<BlogContext>(options =>
481+
options.LogTo(Console.WriteLine));
482+
483+
services.AddDbContext<BlogContext>(options =>
484+
options.UseInMemoryDatabase("CompositionExample"));
485+
486+
services.ConfigureDbContext<BlogContext>(options =>
487+
options.EnableSensitiveDataLogging());
488+
489+
var serviceProvider = services.BuildServiceProvider();
490+
-->
491+
[!code-csharp[ConfigurationComposition](../../../samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs?name=ConfigurationComposition)]
492+
493+
For conflicting options, the last configuration wins. See [breaking changes in EF Core 8.0](xref:core/what-is-new/ef-core-8.0/breaking-changes#AddDbContext) for more information about this behavior change.
494+
495+
> [!NOTE]
496+
> Configuring a different provider will not remove the previous provider configuration. This can lead to errors when creating the context. To completely replace the provider, you need to remove the context registration and re-add it, or create a new service collection.
497+
498+
<!-- See also [Using Dependency Injection](TODO) for advanced dependency injection configuration with EF Core. -->
499+
428500
## More reading
429501

430502
- Read [Dependency Injection](/aspnet/core/fundamentals/dependency-injection) to learn more about using DI.

entity-framework/core/what-is-new/ef-core-8.0/breaking-changes.md

Lines changed: 35 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -627,19 +627,50 @@ protected override void OnModelCreating(ModelBuilder modelBuilder)
627627
628628
#### Old behavior
629629

630-
Previously, when multiple calls to `AddDbContext`, `AddDbContextPool`, `AddDbContextFactory` or `AddPooledDbContextFactor` were made with the same context type but conflicting configuration, the first one won.
630+
Previously, when multiple calls to `AddDbContext`, `AddDbContextPool`, `AddDbContextFactory` or `AddPooledDbContextFactory` were made with the same context type but conflicting configuration, the first one won.
631631

632632
#### New behavior
633633

634-
Starting with EF Core 8.0, the configuration from the last call one will take precedence.
634+
Starting with EF Core 8.0, the configuration from the last call will take precedence.
635635

636636
#### Why
637637

638-
This was changed to be consistent with the new method `ConfigureDbContext` that can be used to add configuration either before or after the `Add*` methods.
638+
This was changed to be consistent with the new method `ConfigureDbContext`, that enables configuration composability for non-conflicting configurations. See [DbContext configuration](xref:core/dbcontext-configuration/index#configuredbcontext) for more information.
639639

640640
#### Mitigations
641641

642-
Reverse the order of `Add*` calls.
642+
If your application depends on the previous behavior where the first registration wins, you have several options:
643+
644+
1. **Reorder your registrations**: Place the registration with the configuration you want to use last:
645+
646+
```csharp
647+
services.AddDbContext<MyContext>(options =>
648+
options.UseSqlServer("connection1")); // This will be ignored now
649+
650+
services.AddDbContext<MyContext>(options =>
651+
options.UseSqlServer("connection2")); // This will be used
652+
```
653+
654+
2. **Remove previous registrations**: If possible, remove the conflicting registration.
655+
656+
3. **Use conditional registration**: Check if the service is already registered before adding:
657+
658+
```csharp
659+
if (!services.Any(d => d.ServiceType == typeof(DbContextOptions<MyContext>)))
660+
{
661+
services.AddDbContext<MyContext>(options =>
662+
options.UseSqlServer("connection"));
663+
}
664+
```
665+
666+
4. **Use the new `ConfigureDbContext` method**: This allows you to configure options without registering the context itself. See [DbContext configuration](xref:core/dbcontext-configuration/index#configuredbcontext) for more information:
667+
668+
```csharp
669+
services.ConfigureDbContext<MyContext>(options =>
670+
options.UseSqlServer("connection"));
671+
672+
services.AddDbContext<MyContext>(); // Register the context without configuration
673+
```
643674

644675
<a name="attributeConventionBase"></a>
645676

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
#nullable enable
2+
3+
using System;
4+
using System.Linq;
5+
using Microsoft.EntityFrameworkCore;
6+
using Microsoft.Extensions.DependencyInjection;
7+
8+
namespace ConfigureDbContextSample;
9+
10+
public class BlogContext : DbContext
11+
{
12+
public BlogContext(DbContextOptions<BlogContext> options) : base(options)
13+
{
14+
}
15+
16+
public DbSet<Blog> Blogs => Set<Blog>();
17+
}
18+
19+
public class Blog
20+
{
21+
public int Id { get; set; }
22+
public required string Title { get; set; }
23+
public string? Content { get; set; }
24+
}
25+
26+
public static class ConfigureDbContextSample
27+
{
28+
public static void Run()
29+
{
30+
BasicConfigureDbContextExample();
31+
ProviderSpecificConfigurationExample();
32+
ConfigurationCompositionExample();
33+
}
34+
35+
private static void BasicConfigureDbContextExample()
36+
{
37+
Console.WriteLine("=== Basic ConfigureDbContext Example ===");
38+
39+
#region BasicConfigureDbContext
40+
var services = new ServiceCollection();
41+
42+
services.ConfigureDbContext<BlogContext>(options =>
43+
options.EnableSensitiveDataLogging()
44+
.EnableDetailedErrors());
45+
46+
services.AddDbContext<BlogContext>(options =>
47+
options.UseInMemoryDatabase("BasicExample"));
48+
#endregion
49+
50+
var serviceProvider = services.BuildServiceProvider();
51+
using var scope = serviceProvider.CreateScope();
52+
var context = scope.ServiceProvider.GetRequiredService<BlogContext>();
53+
54+
Console.WriteLine($"Context configured with provider: {context.Database.ProviderName}");
55+
Console.WriteLine();
56+
}
57+
58+
private static void ProviderSpecificConfigurationExample()
59+
{
60+
Console.WriteLine("=== Provider-Specific Configuration Example ===");
61+
62+
#region ProviderSpecificConfiguration
63+
var services = new ServiceCollection();
64+
65+
services.ConfigureDbContext<BlogContext>(options =>
66+
options.UseSqlServer(sqlOptions =>
67+
sqlOptions.EnableRetryOnFailure()));
68+
69+
services.AddDbContext<BlogContext>(options =>
70+
options.UseSqlServer("connectionString"));
71+
#endregion
72+
73+
var serviceProvider = services.BuildServiceProvider();
74+
Console.WriteLine("Provider-specific configuration applied");
75+
Console.WriteLine();
76+
}
77+
78+
private static void ConfigurationCompositionExample()
79+
{
80+
Console.WriteLine("=== Configuration Composition Example ===");
81+
82+
#region ConfigurationComposition
83+
var services = new ServiceCollection();
84+
85+
services.ConfigureDbContext<BlogContext>(options =>
86+
options.LogTo(Console.WriteLine));
87+
88+
services.AddDbContext<BlogContext>(options =>
89+
options.UseInMemoryDatabase("CompositionExample"));
90+
91+
services.ConfigureDbContext<BlogContext>(options =>
92+
options.EnableSensitiveDataLogging());
93+
#endregion
94+
95+
var serviceProvider = services.BuildServiceProvider();
96+
using var scope = serviceProvider.CreateScope();
97+
var context = scope.ServiceProvider.GetRequiredService<BlogContext>();
98+
99+
Console.WriteLine($"Context configured with provider: {context.Database.ProviderName}");
100+
Console.WriteLine();
101+
}
102+
}

samples/core/Miscellaneous/ConfiguringDbContext/ConfiguringDbContext.csproj

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
<PackageReference Include="Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore" Version="10.0.0" />
1111
<PackageReference Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.0" />
1212
<PackageReference Include="Microsoft.AspNetCore.Identity.UI" Version="10.0.0" />
13+
<PackageReference Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
1314
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
1415
</ItemGroup>
1516

0 commit comments

Comments
 (0)