From feaee3a4b91a3bd81cf8551402ccc9a1bf0e019b Mon Sep 17 00:00:00 2001 From: Jiri Cincura Date: Thu, 8 Aug 2024 10:42:55 +0200 Subject: [PATCH 1/2] Progress --- ...ServerDbContextOptionsBuilderExtensions.cs | 163 +++++++++++------- .../SqlServerServiceCollectionExtensions.cs | 14 +- .../AzureSqlDbContextOptionsBuilder.cs | 122 +++++++++++++ .../AzureSynapseDbContextOptionsBuilder.cs | 122 +++++++++++++ .../SqlEngineDbContextOptionsBuilder.cs | 107 ++++++++++++ .../SqlServerDbContextOptionsBuilder.cs | 50 +----- ...ComplexNavigationsQuerySqlServer160Test.cs | 2 +- ...igationsSharedTypeQuerySqlServer160Test.cs | 2 +- ...rthwindDbFunctionsQuerySqlServer160Test.cs | 2 +- ...NorthwindFunctionsQuerySqlServer160Test.cs | 2 +- ...piledSqlPregenerationQuerySqlServerTest.cs | 2 +- ...imitiveCollectionsQueryOldSqlServerTest.cs | 2 +- ...imitiveCollectionsQuerySqlServer160Test.cs | 2 +- 13 files changed, 475 insertions(+), 117 deletions(-) create mode 100644 src/EFCore.SqlServer/Infrastructure/AzureSqlDbContextOptionsBuilder.cs create mode 100644 src/EFCore.SqlServer/Infrastructure/AzureSynapseDbContextOptionsBuilder.cs create mode 100644 src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs diff --git a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs index 5e5ecf8127d..20dce04c435 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs @@ -34,7 +34,7 @@ public static class SqlServerDbContextOptionsExtensions /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -57,7 +57,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// The builder being used to configure the context. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -88,7 +88,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// state then EF will open and close the connection as needed. The caller owns the connection and is /// responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -116,7 +116,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// dispose it in the same way it would dispose a connection created by EF. If , then the caller still /// owns the connection and is responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -151,7 +151,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -171,7 +171,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// The type of context to be configured. /// The builder being used to configure the context. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -198,7 +198,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// state then EF will open and close the connection as needed. The caller owns the connection and is /// responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -229,7 +229,7 @@ public static DbContextOptionsBuilder UseSqlServer( /// dispose it in the same way it would dispose a connection created by EF. If , then the caller still /// owns the connection and is responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseSqlServer( this DbContextOptionsBuilder optionsBuilder, @@ -257,17 +257,17 @@ public static DbContextOptionsBuilder UseSqlServer( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) { var extension = GetOrCreateExtension(optionsBuilder); extension = extension .WithEngineType(SqlServerEngineType.AzureSql); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, azureSqlOptionsAction); } /// @@ -280,19 +280,19 @@ public static DbContextOptionsBuilder UseAzureSql( /// /// The builder being used to configure the context. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, string? connectionString, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) { var extension = GetOrCreateExtension(optionsBuilder); extension = (SqlServerOptionsExtension)extension .WithEngineType(SqlServerEngineType.AzureSql) .WithConnectionString(connectionString); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, azureSqlOptionsAction); } // Note: Decision made to use DbConnection not SqlConnection: Issue #772 @@ -311,13 +311,13 @@ public static DbContextOptionsBuilder UseAzureSql( /// state then EF will open and close the connection as needed. The caller owns the connection and is /// responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, - Action? sqlServerOptionsAction = null) - => UseAzureSql(optionsBuilder, connection, false, sqlServerOptionsAction); + Action? azureSqlOptionsAction = null) + => UseAzureSql(optionsBuilder, connection, false, azureSqlOptionsAction); // Note: Decision made to use DbConnection not SqlConnection: Issue #772 /// @@ -339,13 +339,13 @@ public static DbContextOptionsBuilder UseAzureSql( /// dispose it in the same way it would dispose a connection created by EF. If , then the caller still /// owns the connection and is responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, bool contextOwnsConnection, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) { Check.NotNull(connection, nameof(connection)); @@ -354,7 +354,7 @@ public static DbContextOptionsBuilder UseAzureSql( .WithEngineType(SqlServerEngineType.AzureSql) .WithConnection(connection, contextOwnsConnection); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, azureSqlOptionsAction); } /// @@ -374,14 +374,14 @@ public static DbContextOptionsBuilder UseAzureSql( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSql( - (DbContextOptionsBuilder)optionsBuilder, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, azureSqlOptionsAction); /// /// Configures the context to connect to a Azure SQL database. @@ -394,15 +394,15 @@ public static DbContextOptionsBuilder UseAzureSql( /// The type of context to be configured. /// The builder being used to configure the context. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, string? connectionString, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSql( - (DbContextOptionsBuilder)optionsBuilder, connectionString, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, connectionString, azureSqlOptionsAction); // Note: Decision made to use DbConnection not SqlConnection: Issue #772 /// @@ -421,15 +421,15 @@ public static DbContextOptionsBuilder UseAzureSql( /// state then EF will open and close the connection as needed. The caller owns the connection and is /// responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSql( - (DbContextOptionsBuilder)optionsBuilder, connection, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, connection, azureSqlOptionsAction); // Note: Decision made to use DbConnection not SqlConnection: Issue #772 /// @@ -452,16 +452,16 @@ public static DbContextOptionsBuilder UseAzureSql( /// dispose it in the same way it would dispose a connection created by EF. If , then the caller still /// owns the connection and is responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSql( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, bool contextOwnsConnection, - Action? sqlServerOptionsAction = null) + Action? azureSqlOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSql( - (DbContextOptionsBuilder)optionsBuilder, connection, contextOwnsConnection, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, connection, contextOwnsConnection, azureSqlOptionsAction); /// /// Configures the context to connect to a Azure Synapse database, but without initially setting any @@ -480,17 +480,17 @@ public static DbContextOptionsBuilder UseAzureSql( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) { var extension = GetOrCreateExtension(optionsBuilder); extension = extension .WithEngineType(SqlServerEngineType.AzureSynapse); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, azureSynapseOptionsAction); } /// @@ -503,19 +503,19 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// /// The builder being used to configure the context. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, string? connectionString, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) { var extension = GetOrCreateExtension(optionsBuilder); extension = (SqlServerOptionsExtension)extension .WithEngineType(SqlServerEngineType.AzureSynapse) .WithConnectionString(connectionString); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, azureSynapseOptionsAction); } // Note: Decision made to use DbConnection not SqlConnection: Issue #772 @@ -534,13 +534,13 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// state then EF will open and close the connection as needed. The caller owns the connection and is /// responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, - Action? sqlServerOptionsAction = null) - => UseAzureSynapse(optionsBuilder, connection, false, sqlServerOptionsAction); + Action? azureSynapseOptionsAction = null) + => UseAzureSynapse(optionsBuilder, connection, false, azureSynapseOptionsAction); // Note: Decision made to use DbConnection not SqlConnection: Issue #772 /// @@ -562,13 +562,13 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// dispose it in the same way it would dispose a connection created by EF. If , then the caller still /// owns the connection and is responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, bool contextOwnsConnection, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) { Check.NotNull(connection, nameof(connection)); @@ -577,7 +577,7 @@ public static DbContextOptionsBuilder UseAzureSynapse( .WithEngineType(SqlServerEngineType.AzureSynapse) .WithConnection(connection, contextOwnsConnection); ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, azureSynapseOptionsAction); } /// @@ -597,14 +597,14 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSynapse( - (DbContextOptionsBuilder)optionsBuilder, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, azureSynapseOptionsAction); /// /// Configures the context to connect to a Azure Synapse database. @@ -617,15 +617,15 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// The type of context to be configured. /// The builder being used to configure the context. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, string? connectionString, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSynapse( - (DbContextOptionsBuilder)optionsBuilder, connectionString, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, connectionString, azureSynapseOptionsAction); // Note: Decision made to use DbConnection not SqlConnection: Issue #772 /// @@ -644,15 +644,15 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// state then EF will open and close the connection as needed. The caller owns the connection and is /// responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSynapse( - (DbContextOptionsBuilder)optionsBuilder, connection, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, connection, azureSynapseOptionsAction); // Note: Decision made to use DbConnection not SqlConnection: Issue #772 /// @@ -675,16 +675,16 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// dispose it in the same way it would dispose a connection created by EF. If , then the caller still /// owns the connection and is responsible for its disposal. /// - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder UseAzureSynapse( this DbContextOptionsBuilder optionsBuilder, DbConnection connection, bool contextOwnsConnection, - Action? sqlServerOptionsAction = null) + Action? azureSynapseOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)UseAzureSynapse( - (DbContextOptionsBuilder)optionsBuilder, connection, contextOwnsConnection, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, connection, contextOwnsConnection, azureSynapseOptionsAction); /// /// Configures the context to connect to any of SQL Server, Azure SQL, Azure Synapse databases, but without initially setting any @@ -703,14 +703,14 @@ public static DbContextOptionsBuilder UseAzureSynapse( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder ConfigureSqlEngine( this DbContextOptionsBuilder optionsBuilder, - Action? sqlServerOptionsAction = null) + Action? sqlEngineOptionsAction = null) { ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(GetOrCreateExtension(optionsBuilder)); - return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction); + return ApplyConfiguration(optionsBuilder, sqlEngineOptionsAction); } /// @@ -730,14 +730,14 @@ public static DbContextOptionsBuilder ConfigureSqlEngine( /// /// /// The builder being used to configure the context. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse configuration. /// The options builder so that further configuration can be chained. public static DbContextOptionsBuilder ConfigureSqlEngine( this DbContextOptionsBuilder optionsBuilder, - Action? sqlServerOptionsAction = null) + Action? sqlEngineOptionsAction = null) where TContext : DbContext => (DbContextOptionsBuilder)ConfigureSqlEngine( - (DbContextOptionsBuilder)optionsBuilder, sqlServerOptionsAction); + (DbContextOptionsBuilder)optionsBuilder, sqlEngineOptionsAction); private static T GetOrCreateExtension(DbContextOptionsBuilder optionsBuilder) where T : RelationalOptionsExtension, new() @@ -757,6 +757,45 @@ private static DbContextOptionsBuilder ApplyConfiguration( return optionsBuilder; } + private static DbContextOptionsBuilder ApplyConfiguration( + DbContextOptionsBuilder optionsBuilder, + Action? azureSqlOptionsAction) + { + ConfigureWarnings(optionsBuilder); + + azureSqlOptionsAction?.Invoke(new AzureSqlDbContextOptionsBuilder(optionsBuilder)); + + var extension = GetOrCreateExtension(optionsBuilder).ApplyDefaults(optionsBuilder.Options); + ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); + + return optionsBuilder; + } + private static DbContextOptionsBuilder ApplyConfiguration( + DbContextOptionsBuilder optionsBuilder, + Action? azureSynapseOptionsAction) + { + ConfigureWarnings(optionsBuilder); + + azureSynapseOptionsAction?.Invoke(new AzureSynapseDbContextOptionsBuilder(optionsBuilder)); + + var extension = GetOrCreateExtension(optionsBuilder).ApplyDefaults(optionsBuilder.Options); + ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); + + return optionsBuilder; + } + private static DbContextOptionsBuilder ApplyConfiguration( + DbContextOptionsBuilder optionsBuilder, + Action? sqlEngineOptionsAction) + { + ConfigureWarnings(optionsBuilder); + + sqlEngineOptionsAction?.Invoke(new SqlEngineDbContextOptionsBuilder(optionsBuilder)); + + var extension = GetOrCreateExtension(optionsBuilder).ApplyDefaults(optionsBuilder.Options); + ((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension); + + return optionsBuilder; + } private static void ConfigureWarnings(DbContextOptionsBuilder optionsBuilder) { diff --git a/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs b/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs index 87f220451ed..7e8c0696546 100644 --- a/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs +++ b/src/EFCore.SqlServer/Extensions/SqlServerServiceCollectionExtensions.cs @@ -52,7 +52,7 @@ public static class SqlServerServiceCollectionExtensions /// The type of context to be registered. /// The to add services to. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional SQL Server specific configuration. /// An optional action to configure the for the context. /// The same service collection so that multiple calls can be chained. public static IServiceCollection AddSqlServer( @@ -126,20 +126,20 @@ public static IServiceCollection AddEntityFrameworkSqlServer(this IServiceCollec /// The type of context to be registered. /// The to add services to. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure SQL specific configuration. /// An optional action to configure the for the context. /// The same service collection so that multiple calls can be chained. public static IServiceCollection AddAzureSql( this IServiceCollection serviceCollection, string? connectionString, - Action? sqlServerOptionsAction = null, + Action? azureSqlOptionsAction = null, Action? optionsAction = null) where TContext : DbContext => serviceCollection.AddDbContext( (_, options) => { optionsAction?.Invoke(options); - options.UseAzureSql(connectionString, sqlServerOptionsAction); + options.UseAzureSql(connectionString, azureSqlOptionsAction); }); /// @@ -200,20 +200,20 @@ public static IServiceCollection AddEntityFrameworkAzureSql(this IServiceCollect /// The type of context to be registered. /// The to add services to. /// The connection string of the database to connect to. - /// An optional action to allow additional SQL Server, Azure SQL, Azure Synapse specific configuration. + /// An optional action to allow additional Azure Synapse specific configuration. /// An optional action to configure the for the context. /// The same service collection so that multiple calls can be chained. public static IServiceCollection AddAzureSynapse( this IServiceCollection serviceCollection, string? connectionString, - Action? sqlServerOptionsAction = null, + Action? azureSynapseOptionsAction = null, Action? optionsAction = null) where TContext : DbContext => serviceCollection.AddDbContext( (_, options) => { optionsAction?.Invoke(options); - options.UseAzureSynapse(connectionString, sqlServerOptionsAction); + options.UseAzureSynapse(connectionString, azureSynapseOptionsAction); }); /// diff --git a/src/EFCore.SqlServer/Infrastructure/AzureSqlDbContextOptionsBuilder.cs b/src/EFCore.SqlServer/Infrastructure/AzureSqlDbContextOptionsBuilder.cs new file mode 100644 index 00000000000..45f2204d354 --- /dev/null +++ b/src/EFCore.SqlServer/Infrastructure/AzureSqlDbContextOptionsBuilder.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Microsoft.EntityFrameworkCore.Infrastructure; + +/// +/// Allows Azure SQL specific configuration to be performed on . +/// +/// +/// Instances of this class are returned from a call to +/// +/// and it is not designed to be directly constructed in your application code. +/// +public class AzureSqlDbContextOptionsBuilder + : RelationalDbContextOptionsBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// The options builder. + public AzureSqlDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) + : base(optionsBuilder) + { + } + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure SQL. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual AzureSqlDbContextOptionsBuilder EnableRetryOnFailure() + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure SQL. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// A default value 30 seconds for the maximum default delay is used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual AzureSqlDbContextOptionsBuilder EnableRetryOnFailure(int maxRetryCount) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure SQL. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + /// Additional SQL error numbers that should be considered transient. + public virtual AzureSqlDbContextOptionsBuilder EnableRetryOnFailure(ICollection errorNumbersToAdd) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, errorNumbersToAdd)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure SQL. It is pre-configured with + /// error numbers for transient errors that can be retried, but additional error numbers can also be supplied. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + /// The maximum number of retry attempts. + /// The maximum delay between retries. + /// Additional SQL error numbers that should be considered transient. + public virtual AzureSqlDbContextOptionsBuilder EnableRetryOnFailure( + int maxRetryCount, + TimeSpan maxRetryDelay, + IEnumerable? errorNumbersToAdd) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount, maxRetryDelay, errorNumbersToAdd)); + + /// + /// Sets the Azure SQL compatibility level that EF Core will use when interacting with the database. This allows configuring EF + /// Core to work with older (or newer) versions of Azure SQL. Defaults to 160. + /// + /// + /// See Using DbContextOptions, and + /// + /// Azure SQL documentation on compatibility level + /// + /// for more information and examples. + /// + /// to have null resource + public virtual AzureSqlDbContextOptionsBuilder UseCompatibilityLevel(int compatibilityLevel) + => WithOption(e => e.WithAzureSqlCompatibilityLevel(compatibilityLevel)); +} diff --git a/src/EFCore.SqlServer/Infrastructure/AzureSynapseDbContextOptionsBuilder.cs b/src/EFCore.SqlServer/Infrastructure/AzureSynapseDbContextOptionsBuilder.cs new file mode 100644 index 00000000000..d3d822e824b --- /dev/null +++ b/src/EFCore.SqlServer/Infrastructure/AzureSynapseDbContextOptionsBuilder.cs @@ -0,0 +1,122 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Microsoft.EntityFrameworkCore.Infrastructure; + +/// +/// Allows Azure Synapse specific configuration to be performed on . +/// +/// +/// Instances of this class are returned from a call to +/// +/// and it is not designed to be directly constructed in your application code. +/// +public class AzureSynapseDbContextOptionsBuilder + : RelationalDbContextOptionsBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// The options builder. + public AzureSynapseDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) + : base(optionsBuilder) + { + } + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual AzureSynapseDbContextOptionsBuilder EnableRetryOnFailure() + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// A default value 30 seconds for the maximum default delay is used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual AzureSynapseDbContextOptionsBuilder EnableRetryOnFailure(int maxRetryCount) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + /// Additional SQL error numbers that should be considered transient. + public virtual AzureSynapseDbContextOptionsBuilder EnableRetryOnFailure(ICollection errorNumbersToAdd) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, errorNumbersToAdd)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried, but additional error numbers can also be supplied. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + /// The maximum number of retry attempts. + /// The maximum delay between retries. + /// Additional SQL error numbers that should be considered transient. + public virtual AzureSynapseDbContextOptionsBuilder EnableRetryOnFailure( + int maxRetryCount, + TimeSpan maxRetryDelay, + IEnumerable? errorNumbersToAdd) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount, maxRetryDelay, errorNumbersToAdd)); + + /// + /// Sets the Azure Synapse compatibility level that EF Core will use when interacting with the database. This allows configuring EF + /// Core to work with older (or newer) versions of Azure Synapse. Defaults to 30. + /// + /// + /// See Using DbContextOptions, and + /// + /// Azure Synapse documentation on compatibility level + /// + /// for more information and examples. + /// + /// to have null resource + public virtual AzureSynapseDbContextOptionsBuilder UseCompatibilityLevel(int compatibilityLevel) + => WithOption(e => e.WithAzureSynapseCompatibilityLevel(compatibilityLevel)); +} diff --git a/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs b/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs new file mode 100644 index 00000000000..4c8de6af66c --- /dev/null +++ b/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs @@ -0,0 +1,107 @@ +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. + +using Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal; + +namespace Microsoft.EntityFrameworkCore.Infrastructure; + +/// +/// Allows SQL Server, Azure SQL, Azure Synapse specific configuration to be performed on . +/// +/// +/// Instances of this class are returned from a call to +/// +/// and it is not designed to be directly constructed in your application code. +/// +public class SqlEngineDbContextOptionsBuilder + : RelationalDbContextOptionsBuilder +{ + /// + /// Initializes a new instance of the class. + /// + /// The options builder. + public SqlEngineDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) + : base(optionsBuilder) + { + } + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual SqlEngineDbContextOptionsBuilder EnableRetryOnFailure() + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// A default value 30 seconds for the maximum default delay is used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual SqlEngineDbContextOptionsBuilder EnableRetryOnFailure(int maxRetryCount) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + /// Additional SQL error numbers that should be considered transient. + public virtual SqlEngineDbContextOptionsBuilder EnableRetryOnFailure(ICollection errorNumbersToAdd) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, errorNumbersToAdd)); + + /// + /// Configures the context to use the default retrying . + /// + /// + /// + /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried, but additional error numbers can also be supplied. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + /// The maximum number of retry attempts. + /// The maximum delay between retries. + /// Additional SQL error numbers that should be considered transient. + public virtual SqlEngineDbContextOptionsBuilder EnableRetryOnFailure( + int maxRetryCount, + TimeSpan maxRetryDelay, + IEnumerable? errorNumbersToAdd) + => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount, maxRetryDelay, errorNumbersToAdd)); +} diff --git a/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs b/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs index 863dcac7192..143ac91bc0f 100644 --- a/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs +++ b/src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs @@ -6,13 +6,11 @@ namespace Microsoft.EntityFrameworkCore.Infrastructure; /// -/// Allows SQL Server, Azure SQL, Azure Synapse specific configuration to be performed on . +/// Allows SQL Server specific configuration to be performed on . /// /// /// Instances of this class are returned from a call to -/// , -/// , -/// +/// /// and it is not designed to be directly constructed in your application code. /// public class SqlServerDbContextOptionsBuilder @@ -32,7 +30,7 @@ public SqlServerDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) /// /// /// - /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// This strategy is specifically tailored to SQL Server. It is pre-configured with /// error numbers for transient errors that can be retried. /// /// @@ -51,7 +49,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure() /// /// /// - /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// This strategy is specifically tailored to SQL Server. It is pre-configured with /// error numbers for transient errors that can be retried. /// /// @@ -70,7 +68,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(int maxRetr /// /// /// - /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// This strategy is specifically tailored to SQL Server. It is pre-configured with /// error numbers for transient errors that can be retried. /// /// @@ -90,7 +88,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(ICollection /// /// /// - /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// This strategy is specifically tailored to SQL Server. It is pre-configured with /// error numbers for transient errors that can be retried, but additional error numbers can also be supplied. /// /// @@ -119,9 +117,9 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure( /// /// for more information and examples. /// - /// to have null resource - public virtual SqlServerDbContextOptionsBuilder UseSqlServerCompatibilityLevel(int sqlServerCompatibilityLevel) - => WithOption(e => e.WithSqlServerCompatibilityLevel(sqlServerCompatibilityLevel)); + /// to have null resource + public virtual SqlServerDbContextOptionsBuilder UseCompatibilityLevel(int compatibilityLevel) + => WithOption(e => e.WithSqlServerCompatibilityLevel(compatibilityLevel)); /// /// Configures the context to use defaults optimized for Azure SQL, including retries on errors. @@ -130,34 +128,4 @@ public virtual SqlServerDbContextOptionsBuilder UseSqlServerCompatibilityLevel(i [Obsolete("Use UseAzureSql instead of UseSqlServer with UseAzureSqlDefaults.")] public virtual SqlServerDbContextOptionsBuilder UseAzureSqlDefaults(bool enable = true) => WithOption(e => e.WithLegacyAzureSql(enable)); - - /// - /// Sets the Azure SQL compatibility level that EF Core will use when interacting with the database. This allows configuring EF - /// Core to work with older (or newer) versions of Azure SQL. Defaults to 160. - /// - /// - /// See Using DbContextOptions, and - /// - /// Azure SQL documentation on compatibility level - /// - /// for more information and examples. - /// - /// to have null resource - public virtual SqlServerDbContextOptionsBuilder UseAzureSqlCompatibilityLevel(int azureSqlCompatibilityLevel) - => WithOption(e => e.WithAzureSqlCompatibilityLevel(azureSqlCompatibilityLevel)); - - /// - /// Sets the Azure Synapse compatibility level that EF Core will use when interacting with the database. This allows configuring EF - /// Core to work with older (or newer) versions of Azure Synapse. Defaults to 30. - /// - /// - /// See Using DbContextOptions, and - /// - /// Azure Synapse documentation on compatibility level - /// - /// for more information and examples. - /// - /// to have null resource - public virtual SqlServerDbContextOptionsBuilder UseAzureSynapseCompatibilityLevel(int azureSynapseCompatibilityLevel) - => WithOption(e => e.WithAzureSynapseCompatibilityLevel(azureSynapseCompatibilityLevel)); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs index c9f9b88881b..ee806b4e0ab 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsQuerySqlServer160Test.cs @@ -4904,6 +4904,6 @@ protected override string StoreName => "ComplexNavigations160"; public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).UseSqlServer(b => b.UseSqlServerCompatibilityLevel(160)); + => base.AddOptions(builder).UseSqlServer(b => b.UseCompatibilityLevel(160)); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs index d58c957f986..83800b92c3b 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/ComplexNavigationsSharedTypeQuerySqlServer160Test.cs @@ -8526,6 +8526,6 @@ protected override string StoreName => "ComplexNavigationsOwned160"; public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).UseSqlServer(b => b.UseSqlServerCompatibilityLevel(160)); + => base.AddOptions(builder).UseSqlServer(b => b.UseCompatibilityLevel(160)); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServer160Test.cs index faaf2217203..c41575cf0ba 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindDbFunctionsQuerySqlServer160Test.cs @@ -1467,6 +1467,6 @@ private void AssertSql(params string[] expected) public class Fixture160 : NorthwindQuerySqlServerFixture { public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).UseSqlServer(b => b.UseSqlServerCompatibilityLevel(160)); + => base.AddOptions(builder).UseSqlServer(b => b.UseCompatibilityLevel(160)); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServer160Test.cs index 6f0172d20f0..555f1733719 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/NorthwindFunctionsQuerySqlServer160Test.cs @@ -3008,6 +3008,6 @@ protected override void ClearLog() public class Fixture160 : NorthwindQuerySqlServerFixture { public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).UseSqlServer(b => b.UseSqlServerCompatibilityLevel(160)); + => base.AddOptions(builder).UseSqlServer(b => b.UseCompatibilityLevel(160)); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs index 93022636239..b187615aee8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrecompiledSqlPregenerationQuerySqlServerTest.cs @@ -258,7 +258,7 @@ public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder build // TODO: Figure out if there's a nice way to continue using the retrying strategy var sqlServerOptionsBuilder = new SqlServerDbContextOptionsBuilder(builder); sqlServerOptionsBuilder - .UseSqlServerCompatibilityLevel(120) + .UseCompatibilityLevel(120) .ExecutionStrategy(d => new NonRetryingExecutionStrategy(d)); return builder; } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index 92b57f0d64d..5877a8c7c14 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -1119,6 +1119,6 @@ protected override ITestStoreFactory TestStoreFactory // Compatibility level 120 (SQL Server 2014) doesn't support OPENJSON public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).UseSqlServer(o => o.UseSqlServerCompatibilityLevel(120)); + => base.AddOptions(builder).UseSqlServer(o => o.UseCompatibilityLevel(120)); } } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs index 6d0e9f7bf9a..09878bccedd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs @@ -1991,7 +1991,7 @@ protected override ITestStoreFactory TestStoreFactory => SqlServerTestStoreFactory.Instance; public override DbContextOptionsBuilder AddOptions(DbContextOptionsBuilder builder) - => base.AddOptions(builder).UseSqlServer(b => b.UseSqlServerCompatibilityLevel(160)); + => base.AddOptions(builder).UseSqlServer(b => b.UseCompatibilityLevel(160)); protected override void OnModelCreating(ModelBuilder modelBuilder, DbContext context) { From ec7799d7587fa14db3d83300b9d7acfe57bd1c42 Mon Sep 17 00:00:00 2001 From: Jiri Cincura Date: Fri, 9 Aug 2024 12:59:52 +0200 Subject: [PATCH 2/2] Progress --- .../Internal/SqlServerOptionsExtension.cs | 55 ++++++++++------ .../SqlEngineDbContextOptionsBuilder.cs | 19 ++++++ .../SqlServerConfigPatternsTest.cs | 62 ++++++++++++++++++- 3 files changed, 116 insertions(+), 20 deletions(-) diff --git a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs index 2fb912637a7..34fb59fc93f 100644 --- a/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs +++ b/src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs @@ -16,10 +16,10 @@ public class SqlServerOptionsExtension : RelationalOptionsExtension, IDbContextO { private DbContextOptionsExtensionInfo? _info; private SqlServerEngineType _engineType; - private bool _legacyAzureSql; private int? _sqlServerCompatibilityLevel; private int? _azureSqlCompatibilityLevel; private int? _azureSynapseCompatibilityLevel; + private bool _useRetryStrategyByDefault; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -80,10 +80,10 @@ protected SqlServerOptionsExtension(SqlServerOptionsExtension copyFrom) : base(copyFrom) { _engineType = copyFrom._engineType; - _legacyAzureSql = copyFrom._legacyAzureSql; _sqlServerCompatibilityLevel = copyFrom._sqlServerCompatibilityLevel; _azureSqlCompatibilityLevel = copyFrom._azureSqlCompatibilityLevel; _azureSynapseCompatibilityLevel = copyFrom._azureSynapseCompatibilityLevel; + _useRetryStrategyByDefault = copyFrom._useRetryStrategyByDefault; } /// @@ -119,8 +119,8 @@ public virtual SqlServerEngineType EngineType /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual bool LegacyAzureSql - => _legacyAzureSql; + public virtual int SqlServerCompatibilityLevel + => _sqlServerCompatibilityLevel ?? SqlServerDefaultCompatibilityLevel; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -128,8 +128,8 @@ public virtual bool LegacyAzureSql /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual int SqlServerCompatibilityLevel - => _sqlServerCompatibilityLevel ?? SqlServerDefaultCompatibilityLevel; + public virtual int AzureSqlCompatibilityLevel + => _azureSqlCompatibilityLevel ?? AzureSqlDefaultCompatibilityLevel; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -137,8 +137,8 @@ public virtual int SqlServerCompatibilityLevel /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual int AzureSqlCompatibilityLevel - => _azureSqlCompatibilityLevel ?? AzureSqlDefaultCompatibilityLevel; + public virtual int AzureSynapseCompatibilityLevel + => _azureSynapseCompatibilityLevel ?? AzureSynapseDefaultCompatibilityLevel; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -146,8 +146,8 @@ public virtual int AzureSqlCompatibilityLevel /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public virtual int AzureSynapseCompatibilityLevel - => _azureSynapseCompatibilityLevel ?? AzureSynapseDefaultCompatibilityLevel; + public virtual bool UseRetryStrategyByDefault + => _useRetryStrategyByDefault; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -180,7 +180,7 @@ public virtual SqlServerOptionsExtension WithLegacyAzureSql(bool enable) var clone = (SqlServerOptionsExtension)Clone(); clone._engineType = SqlServerEngineType.SqlServer; - clone._legacyAzureSql = enable; + clone._useRetryStrategyByDefault = enable; return clone; } @@ -230,14 +230,31 @@ public virtual SqlServerOptionsExtension WithAzureSynapseCompatibilityLevel(int? return clone; } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + public virtual SqlServerOptionsExtension WithUseRetryStrategyByDefault(bool enable) + { + var clone = (SqlServerOptionsExtension)Clone(); + + clone._useRetryStrategyByDefault = enable; + + return clone; + } + /// public virtual IDbContextOptionsExtension ApplyDefaults(IDbContextOptions options) { if (ExecutionStrategyFactory == null - && (EngineType == SqlServerEngineType.AzureSql || EngineType == SqlServerEngineType.AzureSynapse || LegacyAzureSql)) - { - return WithExecutionStrategyFactory(c => new SqlServerRetryingExecutionStrategy(c)); - } + && (EngineType == SqlServerEngineType.AzureSql + || EngineType == SqlServerEngineType.AzureSynapse + || UseRetryStrategyByDefault)) + { + return WithExecutionStrategyFactory(c => new SqlServerRetryingExecutionStrategy(c)); + } return this; } @@ -313,11 +330,11 @@ public override string LogFragment .Append(Extension._engineType) .Append(' '); - if (Extension._legacyAzureSql) + if (Extension._useRetryStrategyByDefault) { builder - .Append("LegacyAzureSql=") - .Append(Extension._legacyAzureSql) + .Append("UseRetryStrategyByDefault=") + .Append(Extension._useRetryStrategyByDefault) .Append(' '); } @@ -353,7 +370,7 @@ public override string LogFragment public override void PopulateDebugInfo(IDictionary debugInfo) { debugInfo["EngineType"] = Extension.EngineType.ToString(); - debugInfo["LegacyAzureSql"] = Extension.LegacyAzureSql.ToString(); + debugInfo["UseRetryStrategyByDefault"] = Extension.UseRetryStrategyByDefault.ToString(); if (Extension.SqlServerCompatibilityLevel is int sqlServerCompatibilityLevel) { diff --git a/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs b/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs index 4c8de6af66c..6c2ea214861 100644 --- a/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs +++ b/src/EFCore.SqlServer/Infrastructure/SqlEngineDbContextOptionsBuilder.cs @@ -44,6 +44,25 @@ public SqlEngineDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder) public virtual SqlEngineDbContextOptionsBuilder EnableRetryOnFailure() => ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c)); + /// + /// Configures the context to use the default retrying unless it is configured explicitly. + /// + /// + /// + /// This strategy is specifically tailored to SQL Server, Azure SQL, Azure Synapse. It is pre-configured with + /// error numbers for transient errors that can be retried. + /// + /// + /// Default values of 6 for the maximum retry count and 30 seconds for the maximum default delay are used. + /// + /// + /// See Connection resiliency and database retries + /// for more information and examples. + /// + /// + public virtual SqlEngineDbContextOptionsBuilder TryEnableRetryOnFailure() + => WithOption(e => e.WithUseRetryStrategyByDefault(true)); + /// /// Configures the context to use the default retrying . /// diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs index 26c498706f9..ed78921e690 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerConfigPatternsTest.cs @@ -732,7 +732,7 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) } } - public class AddConfigureDbContext + public class AddConfigureDbContextWithRetry { [Fact] public void Does_not_throw_for_Add_Configure() @@ -764,6 +764,66 @@ public void Proper_execution_strategy() Assert.IsType(context.Database.CreateExecutionStrategy()); } + [Fact] + public void Fallback_execution_strategy_used() + { + using var scope = new ServiceCollection() + .AddDbContext(b => b.UseSqlServer()) + .ConfigureDbContext(b => b.ConfigureSqlEngine(o => o.TryEnableRetryOnFailure())) + .BuildServiceProvider(validateScopes: true) + .CreateScope(); + + var serviceProvider = scope.ServiceProvider; + + var context = serviceProvider.GetRequiredService(); + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + + [Fact] + public void Fallback_execution_strategy_not_used() + { + using var scope = new ServiceCollection() + .AddDbContext(b => b.UseSqlServer()) + .ConfigureDbContext(b => b.ConfigureSqlEngine()) + .BuildServiceProvider(validateScopes: true) + .CreateScope(); + + var serviceProvider = scope.ServiceProvider; + + var context = serviceProvider.GetRequiredService(); + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + + [Fact] + public void Fallback_execution_strategy_does_not_overwrite_Add_first() + { + using var scope = new ServiceCollection() + .AddDbContext(b => b.UseSqlServer(o => o.ExecutionStrategy(_ => new DummyExecutionStrategy()))) + .ConfigureDbContext(b => b.ConfigureSqlEngine(o => o.TryEnableRetryOnFailure())) + .BuildServiceProvider(validateScopes: true) + .CreateScope(); + + var serviceProvider = scope.ServiceProvider; + + var context = serviceProvider.GetRequiredService(); + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + + [Fact] + public void Fallback_execution_strategy_does_not_overwrite_Configure_first() + { + using var scope = new ServiceCollection() + .ConfigureDbContext(b => b.ConfigureSqlEngine(o => o.TryEnableRetryOnFailure())) + .AddDbContext(b => b.UseSqlServer(o => o.ExecutionStrategy(_ => new DummyExecutionStrategy()))) + .BuildServiceProvider(validateScopes: true) + .CreateScope(); + + var serviceProvider = scope.ServiceProvider; + + var context = serviceProvider.GetRequiredService(); + Assert.IsType(context.Database.CreateExecutionStrategy()); + } + private class NorthwindContext : DbContext { public DbSet Customers { get; set; }