diff --git a/entity-framework/core/dbcontext-configuration/index.md b/entity-framework/core/dbcontext-configuration/index.md index 55d3738ccb..a0ce9cba32 100644 --- a/entity-framework/core/dbcontext-configuration/index.md +++ b/entity-framework/core/dbcontext-configuration/index.md @@ -2,7 +2,7 @@ title: DbContext Lifetime, Configuration, and Initialization - EF Core description: Patterns for creating and managing DbContext instances with or without dependency injection author: SamMonoRT -ms.date: 11/07/2020 +ms.date: 09/30/2025 uid: core/dbcontext-configuration/index --- @@ -90,6 +90,75 @@ The final result is an `ApplicationDbContext` instance created for each request 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. + + +## ConfigureDbContext for configuration composition + +Starting with EF Core 9.0, you can use 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. + +### Basic ConfigureDbContext usage + +`ConfigureDbContext` allows you to add configuration in a reusable library or component without replacing the entire provider configuration: + + +[!code-csharp[BasicConfigureDbContext](../../../samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs?name=BasicConfigureDbContext)] + +### Provider-specific configuration without connection strings + +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. + + +[!code-csharp[ProviderSpecificConfiguration](../../../samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs?name=ProviderSpecificConfiguration)] + +### ConfigureDbContext and AddDbContext precedence + +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. + +For non-conflicting options (like adding logging, interceptors, or other settings), all configurations are composed together: + + +[!code-csharp[ConfigurationComposition](../../../samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs?name=ConfigurationComposition)] + +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. + +> [!NOTE] +> 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. + ## Basic DbContext initialization with 'new' diff --git a/entity-framework/core/what-is-new/ef-core-8.0/breaking-changes.md b/entity-framework/core/what-is-new/ef-core-8.0/breaking-changes.md index 95795d99fd..4ff9378494 100644 --- a/entity-framework/core/what-is-new/ef-core-8.0/breaking-changes.md +++ b/entity-framework/core/what-is-new/ef-core-8.0/breaking-changes.md @@ -627,19 +627,50 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #### Old behavior -Previously, when multiple calls to `AddDbContext`, `AddDbContextPool`, `AddDbContextFactory` or `AddPooledDbContextFactor` were made with the same context type but conflicting configuration, the first one won. +Previously, when multiple calls to `AddDbContext`, `AddDbContextPool`, `AddDbContextFactory` or `AddPooledDbContextFactory` were made with the same context type but conflicting configuration, the first one won. #### New behavior -Starting with EF Core 8.0, the configuration from the last call one will take precedence. +Starting with EF Core 8.0, the configuration from the last call will take precedence. #### Why -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. +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. #### Mitigations -Reverse the order of `Add*` calls. +If your application depends on the previous behavior where the first registration wins, you have several options: + +1. **Reorder your registrations**: Place the registration with the configuration you want to use last: + + ```csharp + services.AddDbContext(options => + options.UseSqlServer("connection1")); // This will be ignored now + + services.AddDbContext(options => + options.UseSqlServer("connection2")); // This will be used + ``` + +2. **Remove previous registrations**: If possible, remove the conflicting registration. + +3. **Use conditional registration**: Check if the service is already registered before adding: + + ```csharp + if (!services.Any(d => d.ServiceType == typeof(DbContextOptions))) + { + services.AddDbContext(options => + options.UseSqlServer("connection")); + } + ``` + +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: + + ```csharp + services.ConfigureDbContext(options => + options.UseSqlServer("connection")); + + services.AddDbContext(); // Register the context without configuration + ``` diff --git a/samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs b/samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs new file mode 100644 index 0000000000..71ad0c4b2e --- /dev/null +++ b/samples/core/Miscellaneous/ConfiguringDbContext/ConfigureDbContextSample.cs @@ -0,0 +1,102 @@ +#nullable enable + +using System; +using System.Linq; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; + +namespace ConfigureDbContextSample; + +public class BlogContext : DbContext +{ + public BlogContext(DbContextOptions options) : base(options) + { + } + + public DbSet Blogs => Set(); +} + +public class Blog +{ + public int Id { get; set; } + public required string Title { get; set; } + public string? Content { get; set; } +} + +public static class ConfigureDbContextSample +{ + public static void Run() + { + BasicConfigureDbContextExample(); + ProviderSpecificConfigurationExample(); + ConfigurationCompositionExample(); + } + + private static void BasicConfigureDbContextExample() + { + Console.WriteLine("=== Basic ConfigureDbContext Example ==="); + + #region BasicConfigureDbContext + var services = new ServiceCollection(); + + services.ConfigureDbContext(options => + options.EnableSensitiveDataLogging() + .EnableDetailedErrors()); + + services.AddDbContext(options => + options.UseInMemoryDatabase("BasicExample")); + #endregion + + var serviceProvider = services.BuildServiceProvider(); + using var scope = serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + Console.WriteLine($"Context configured with provider: {context.Database.ProviderName}"); + Console.WriteLine(); + } + + private static void ProviderSpecificConfigurationExample() + { + Console.WriteLine("=== Provider-Specific Configuration Example ==="); + + #region ProviderSpecificConfiguration + var services = new ServiceCollection(); + + services.ConfigureDbContext(options => + options.UseSqlServer(sqlOptions => + sqlOptions.EnableRetryOnFailure())); + + services.AddDbContext(options => + options.UseSqlServer("connectionString")); + #endregion + + var serviceProvider = services.BuildServiceProvider(); + Console.WriteLine("Provider-specific configuration applied"); + Console.WriteLine(); + } + + private static void ConfigurationCompositionExample() + { + Console.WriteLine("=== Configuration Composition Example ==="); + + #region ConfigurationComposition + var services = new ServiceCollection(); + + services.ConfigureDbContext(options => + options.LogTo(Console.WriteLine)); + + services.AddDbContext(options => + options.UseInMemoryDatabase("CompositionExample")); + + services.ConfigureDbContext(options => + options.EnableSensitiveDataLogging()); + #endregion + + var serviceProvider = services.BuildServiceProvider(); + using var scope = serviceProvider.CreateScope(); + var context = scope.ServiceProvider.GetRequiredService(); + + Console.WriteLine($"Context configured with provider: {context.Database.ProviderName}"); + Console.WriteLine(); + } +} \ No newline at end of file diff --git a/samples/core/Miscellaneous/ConfiguringDbContext/ConfiguringDbContext.csproj b/samples/core/Miscellaneous/ConfiguringDbContext/ConfiguringDbContext.csproj index 5c4f6ce07a..a59c8ca8d1 100644 --- a/samples/core/Miscellaneous/ConfiguringDbContext/ConfiguringDbContext.csproj +++ b/samples/core/Miscellaneous/ConfiguringDbContext/ConfiguringDbContext.csproj @@ -1,16 +1,19 @@  - net8.0 + net9.0 enable - - - - + + + + + + +