Skip to content

Commit f69cbe8

Browse files
author
msftbot[bot]
authored
Merge pull request #31636 from dotnet-maestro-bot/merge/release/8.0-to-main
[automated] Merge branch 'release/8.0' => 'main'
2 parents a6f0829 + f290667 commit f69cbe8

File tree

9 files changed

+729
-26
lines changed

9 files changed

+729
-26
lines changed

src/EFCore.SqlServer/Extensions/SqlServerDbContextOptionsBuilderExtensions.cs

Lines changed: 15 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -41,11 +41,7 @@ public static DbContextOptionsBuilder UseSqlServer(
4141
{
4242
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(GetOrCreateExtension(optionsBuilder));
4343

44-
ConfigureWarnings(optionsBuilder);
45-
46-
sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder));
47-
48-
return optionsBuilder;
44+
return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction);
4945
}
5046

5147
/// <summary>
@@ -68,11 +64,7 @@ public static DbContextOptionsBuilder UseSqlServer(
6864
var extension = (SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).WithConnectionString(connectionString);
6965
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
7066

71-
ConfigureWarnings(optionsBuilder);
72-
73-
sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder));
74-
75-
return optionsBuilder;
67+
return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction);
7668
}
7769

7870
// Note: Decision made to use DbConnection not SqlConnection: Issue #772
@@ -132,11 +124,7 @@ public static DbContextOptionsBuilder UseSqlServer(
132124
var extension = (SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).WithConnection(connection, contextOwnsConnection);
133125
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
134126

135-
ConfigureWarnings(optionsBuilder);
136-
137-
sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder));
138-
139-
return optionsBuilder;
127+
return ApplyConfiguration(optionsBuilder, sqlServerOptionsAction);
140128
}
141129

142130
/// <summary>
@@ -249,6 +237,18 @@ private static SqlServerOptionsExtension GetOrCreateExtension(DbContextOptionsBu
249237
=> optionsBuilder.Options.FindExtension<SqlServerOptionsExtension>()
250238
?? new SqlServerOptionsExtension();
251239

240+
private static DbContextOptionsBuilder ApplyConfiguration(DbContextOptionsBuilder optionsBuilder, Action<SqlServerDbContextOptionsBuilder>? sqlServerOptionsAction)
241+
{
242+
ConfigureWarnings(optionsBuilder);
243+
244+
sqlServerOptionsAction?.Invoke(new SqlServerDbContextOptionsBuilder(optionsBuilder));
245+
246+
var extension = (SqlServerOptionsExtension)GetOrCreateExtension(optionsBuilder).ApplyDefaults(optionsBuilder.Options);
247+
((IDbContextOptionsBuilderInfrastructure)optionsBuilder).AddOrUpdateExtension(extension);
248+
249+
return optionsBuilder;
250+
}
251+
252252
private static void ConfigureWarnings(DbContextOptionsBuilder optionsBuilder)
253253
{
254254
var coreOptionsExtension

src/EFCore.SqlServer/Infrastructure/Internal/SqlServerOptionsExtension.cs

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,10 +11,11 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
1111
/// any release. You should only use it directly in your code with extreme caution and knowing that
1212
/// doing so can result in application failures when updating to a new Entity Framework Core release.
1313
/// </summary>
14-
public class SqlServerOptionsExtension : RelationalOptionsExtension
14+
public class SqlServerOptionsExtension : RelationalOptionsExtension, IDbContextOptionsExtension
1515
{
1616
private DbContextOptionsExtensionInfo? _info;
1717
private int? _compatibilityLevel;
18+
private bool? _azureSql;
1819

1920
/// <summary>
2021
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
@@ -52,6 +53,7 @@ protected SqlServerOptionsExtension(SqlServerOptionsExtension copyFrom)
5253
: base(copyFrom)
5354
{
5455
_compatibilityLevel = copyFrom._compatibilityLevel;
56+
_azureSql = copyFrom._azureSql;
5557
}
5658

5759
/// <summary>
@@ -105,6 +107,50 @@ public virtual SqlServerOptionsExtension WithCompatibilityLevel(int? compatibili
105107
return clone;
106108
}
107109

110+
/// <summary>
111+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
112+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
113+
/// any release. You should only use it directly in your code with extreme caution and knowing that
114+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
115+
/// </summary>
116+
public virtual bool IsAzureSql
117+
=> _azureSql ?? IsAzureSqlDefault;
118+
119+
private bool IsAzureSqlDefault
120+
=> (ConnectionString ?? Connection?.ConnectionString)
121+
?.Contains(".database.windows.net", StringComparison.InvariantCultureIgnoreCase) == true;
122+
123+
/// <summary>
124+
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
125+
/// the same compatibility standards as public APIs. It may be changed or removed without notice in
126+
/// any release. You should only use it directly in your code with extreme caution and knowing that
127+
/// doing so can result in application failures when updating to a new Entity Framework Core release.
128+
/// </summary>
129+
public virtual SqlServerOptionsExtension WithAzureSql(bool enable)
130+
{
131+
var clone = (SqlServerOptionsExtension)Clone();
132+
133+
clone._azureSql = enable;
134+
135+
return clone;
136+
}
137+
138+
/// <inheritdoc/>
139+
public virtual IDbContextOptionsExtension ApplyDefaults(IDbContextOptions options)
140+
{
141+
if (!IsAzureSql)
142+
{
143+
return this;
144+
}
145+
146+
if (ExecutionStrategyFactory == null)
147+
{
148+
return WithExecutionStrategyFactory(c => new SqlServerRetryingExecutionStrategy(c));
149+
}
150+
151+
return this;
152+
}
153+
108154
/// <summary>
109155
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
110156
/// the same compatibility standards as public APIs. It may be changed or removed without notice in

src/EFCore.SqlServer/Infrastructure/SqlServerDbContextOptionsBuilder.cs

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ public SqlServerDbContextOptionsBuilder(DbContextOptionsBuilder optionsBuilder)
2929
/// </summary>
3030
/// <remarks>
3131
/// <para>
32-
/// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with
32+
/// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with
3333
/// error numbers for transient errors that can be retried.
3434
/// </para>
3535
/// <para>
@@ -48,7 +48,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure()
4848
/// </summary>
4949
/// <remarks>
5050
/// <para>
51-
/// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with
51+
/// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with
5252
/// error numbers for transient errors that can be retried.
5353
/// </para>
5454
/// <para>
@@ -67,7 +67,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(int maxRetr
6767
/// </summary>
6868
/// <remarks>
6969
/// <para>
70-
/// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with
70+
/// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with
7171
/// error numbers for transient errors that can be retried.
7272
/// </para>
7373
/// <para>
@@ -87,7 +87,7 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(ICollection
8787
/// </summary>
8888
/// <remarks>
8989
/// <para>
90-
/// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with
90+
/// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with
9191
/// error numbers for transient errors that can be retried, but additional error numbers can also be supplied.
9292
/// </para>
9393
/// <para>
@@ -116,4 +116,11 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(
116116
/// <param name="compatibilityLevel"><see langword="false" /> to have null resource</param>
117117
public virtual SqlServerDbContextOptionsBuilder UseCompatibilityLevel(int compatibilityLevel)
118118
=> WithOption(e => e.WithCompatibilityLevel(compatibilityLevel));
119+
120+
/// <summary>
121+
/// Configures the context to use defaults optimized for Azure SQL, including retries on errors.
122+
/// </summary>
123+
/// <param name="enable">Whether the defaults should be enabled.</param>
124+
public virtual SqlServerDbContextOptionsBuilder UseAzureSql(bool enable = true)
125+
=> WithOption(e => e.WithAzureSql(enable));
119126
}

src/EFCore.SqlServer/SqlServerRetryingExecutionStrategy.cs

Lines changed: 68 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore;
1212
/// </summary>
1313
/// <remarks>
1414
/// <para>
15-
/// This strategy is specifically tailored to SQL Server (including SQL Azure). It is pre-configured with
15+
/// This strategy is specifically tailored to SQL Server (including Azure SQL). It is pre-configured with
1616
/// error numbers for transient errors that can be retried. Additional error numbers to retry on can also be supplied.
1717
/// </para>
1818
/// <para>
@@ -30,6 +30,11 @@ public class SqlServerRetryingExecutionStrategy : ExecutionStrategy
3030
{
3131
private readonly HashSet<int>? _additionalErrorNumbers;
3232

33+
/// <summary>
34+
/// The default minimum time delay between retries for Azure SQL.
35+
/// </summary>
36+
protected static readonly TimeSpan DefaultMinDelayAzureSql = TimeSpan.FromSeconds(5);
37+
3338
/// <summary>
3439
/// Creates a new instance of <see cref="SqlServerRetryingExecutionStrategy" />.
3540
/// </summary>
@@ -138,6 +143,11 @@ public SqlServerRetryingExecutionStrategy(
138143
_additionalErrorNumbers = errorNumbersToAdd?.ToHashSet();
139144
}
140145

146+
/// <summary>
147+
/// Additional SQL error numbers that should be considered transient.
148+
/// </summary>
149+
public virtual IEnumerable<int>? AdditionalErrorNumbers => _additionalErrorNumbers;
150+
141151
/// <summary>
142152
/// Determines whether the specified exception represents a transient failure that can be
143153
/// compensated by a retry. Additional exceptions to retry on can be passed to the constructor.
@@ -181,7 +191,9 @@ protected override bool ShouldRetryOn(Exception exception)
181191

182192
return CallOnWrappedException(lastException, IsMemoryOptimizedError)
183193
? TimeSpan.FromMilliseconds(baseDelay.Value.TotalSeconds)
184-
: baseDelay;
194+
: CallOnWrappedException(lastException, IsThrottlingError)
195+
? baseDelay + DefaultMinDelayAzureSql
196+
: baseDelay;
185197
}
186198

187199
private static bool IsMemoryOptimizedError(Exception exception)
@@ -204,4 +216,58 @@ private static bool IsMemoryOptimizedError(Exception exception)
204216

205217
return false;
206218
}
219+
220+
private static bool IsThrottlingError(Exception exception)
221+
{
222+
if (exception is SqlException sqlException)
223+
{
224+
foreach (SqlError err in sqlException.Errors)
225+
{
226+
switch (err.Number)
227+
{
228+
case 49977:
229+
case 49920:
230+
case 49919:
231+
case 49918:
232+
case 45319:
233+
case 45182:
234+
case 45161:
235+
case 45157:
236+
case 45156:
237+
case 41840:
238+
case 41823:
239+
case 40903:
240+
case 40890:
241+
case 40675:
242+
case 40648:
243+
case 40642:
244+
case 40613:
245+
case 40501:
246+
case 40189:
247+
case 39110:
248+
case 39108:
249+
case 37327:
250+
case 30085:
251+
case 25740:
252+
case 25738:
253+
case 22498:
254+
case 22335:
255+
case 17889:
256+
case 14355:
257+
case 10930:
258+
case 10929:
259+
case 9985:
260+
case 3950:
261+
case 3935:
262+
case 1404:
263+
case 1204:
264+
case 233:
265+
case -2:
266+
return true;
267+
}
268+
}
269+
}
270+
271+
return false;
272+
}
207273
}

0 commit comments

Comments
 (0)