Skip to content

Commit

Permalink
Implement SQL Server compatibility level
Browse files Browse the repository at this point in the history
  • Loading branch information
roji committed Apr 14, 2023
1 parent c0d7628 commit e76605e
Show file tree
Hide file tree
Showing 5 changed files with 178 additions and 2 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -127,8 +127,10 @@ public static IServiceCollection AddEntityFrameworkSqlServer(this IServiceCollec
.TryAdd<INavigationExpansionExtensibilityHelper, SqlServerNavigationExpansionExtensibilityHelper>()
.TryAdd<IQueryableMethodTranslatingExpressionVisitorFactory, SqlServerQueryableMethodTranslatingExpressionVisitorFactory>()
.TryAdd<IExceptionDetector, SqlServerExceptionDetector>()
.TryAdd<ISingletonOptions, ISqlServerSingletonOptions>(p => p.GetRequiredService<ISqlServerSingletonOptions>())
.TryAddProviderSpecificServices(
b => b
.TryAddSingleton<ISqlServerSingletonOptions, SqlServerSingletonOptions>()
.TryAddSingleton<ISqlServerValueGeneratorCache, SqlServerValueGeneratorCache>()
.TryAddSingleton<ISqlServerUpdateSqlGenerator, SqlServerUpdateSqlGenerator>()
.TryAddSingleton<ISqlServerSequenceValueGeneratorFactory, SqlServerSequenceValueGeneratorFactory>()
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;

/// <summary>
/// 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.
/// </summary>
public interface ISqlServerSingletonOptions : ISingletonOptions
{
/// <summary>
/// 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.
/// </summary>
int CompatibilityLevel { get; }

/// <summary>
/// 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.
/// </summary>
int? CompatibilityLevelWithoutDefault { get; }
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,22 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;
public class SqlServerOptionsExtension : RelationalOptionsExtension
{
private DbContextOptionsExtensionInfo? _info;
private int? _compatibilityLevel;

/// <summary>
/// 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.
/// </summary>
// See https://learn.microsoft.com/sql/t-sql/statements/alter-database-transact-sql-compatibility-level
// SQL Server 2022 (16.x): compatibility level 160, start date 2022-11-16, mainstream end date 2028-01-11, extended end date 2033-01-11
// SQL Server 2019 (15.x): compatibility level 150, start date 2019-11-04, mainstream end date 2025-02-28, extended end date 2030-01-08
// SQL Server 2017 (14.x): compatibility level 140, start date 2017-09-29, mainstream end date 2022-10-11, extended end date 2027-10-12
// SQL Server 2016 (13.x): compatibility level 130, start date 2016-06-01, mainstream end date 2021-07-13, extended end date 2026-07-14
// SQL Server 2014 (12.x): compatibility level 120, start date 2014-06-05, mainstream end date 2019-07-09, extended end date 2024-07-09
// TODO: Is 150 OK as a default?
private static readonly int DefaultCompatibilityLevel = 150;

/// <summary>
/// This is an internal API that supports the Entity Framework Core infrastructure and not subject to
Expand All @@ -36,6 +52,7 @@ public SqlServerOptionsExtension()
protected SqlServerOptionsExtension(SqlServerOptionsExtension copyFrom)
: base(copyFrom)
{
_compatibilityLevel = copyFrom._compatibilityLevel;
}

/// <summary>
Expand All @@ -56,6 +73,39 @@ public override DbContextOptionsExtensionInfo Info
protected override RelationalOptionsExtension Clone()
=> new SqlServerOptionsExtension(this);

/// <summary>
/// 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.
/// </summary>
public virtual int CompatibilityLevel
=> _compatibilityLevel ?? DefaultCompatibilityLevel;

/// <summary>
/// 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.
/// </summary>
public virtual int? CompatibilityLevelWithoutDefault
=> _compatibilityLevel;

/// <summary>
/// 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.
/// </summary>
public virtual SqlServerOptionsExtension WithCompatibilityLevel(int? compatibilityLevel)
{
var clone = (SqlServerOptionsExtension)Clone();

clone._compatibilityLevel = compatibilityLevel;

return clone;
}

/// <summary>
/// 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
Expand All @@ -81,7 +131,8 @@ public override bool IsDatabaseProvider
=> true;

public override bool ShouldUseSameServiceProvider(DbContextOptionsExtensionInfo other)
=> other is ExtensionInfo;
=> other is ExtensionInfo otherInfo
&& Extension.CompatibilityLevel == otherInfo.Extension.CompatibilityLevel;

public override string LogFragment
{
Expand All @@ -93,6 +144,13 @@ public override string LogFragment

builder.Append(base.LogFragment);

if (Extension._compatibilityLevel is int compatibilityLevel)
{
builder
.Append("CompatibilityLevel=")
.Append(compatibilityLevel);
}

_logFragment = builder.ToString();
}

Expand All @@ -101,6 +159,13 @@ public override string LogFragment
}

public override void PopulateDebugInfo(IDictionary<string, string> debugInfo)
=> debugInfo["SqlServer"] = "1";
{
debugInfo["SqlServer"] = "1";

if (Extension.CompatibilityLevel is int compatibilityLevel)
{
debugInfo["CompatibilityLevel"] = compatibilityLevel.ToString();
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.

namespace Microsoft.EntityFrameworkCore.SqlServer.Infrastructure.Internal;

/// <summary>
/// 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.
/// </summary>
public class SqlServerSingletonOptions : ISqlServerSingletonOptions
{
/// <summary>
/// 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.
/// </summary>
public virtual int CompatibilityLevel { get; private set; }

/// <summary>
/// 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.
/// </summary>
public virtual int? CompatibilityLevelWithoutDefault { get; private set; }

/// <summary>
/// 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.
/// </summary>
public virtual void Initialize(IDbContextOptions options)
{
var sqlServerOptions = options.FindExtension<SqlServerOptionsExtension>();
if (sqlServerOptions != null)
{
CompatibilityLevel = sqlServerOptions.CompatibilityLevel;
CompatibilityLevelWithoutDefault = sqlServerOptions.CompatibilityLevelWithoutDefault;
}
}

/// <summary>
/// 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.
/// </summary>
public virtual void Validate(IDbContextOptions options)
{
var sqlserverOptions = options.FindExtension<SqlServerOptionsExtension>();

if (sqlserverOptions != null &&
(CompatibilityLevelWithoutDefault != sqlserverOptions.CompatibilityLevelWithoutDefault
|| CompatibilityLevel != sqlserverOptions.CompatibilityLevel))
{
throw new InvalidOperationException(
CoreStrings.SingletonOptionChanged(
nameof(SqlServerDbContextOptionsExtensions.UseSqlServer),
nameof(DbContextOptionsBuilder.UseInternalServiceProvider)));
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,4 +103,18 @@ public virtual SqlServerDbContextOptionsBuilder EnableRetryOnFailure(
TimeSpan maxRetryDelay,
IEnumerable<int>? errorNumbersToAdd)
=> ExecutionStrategy(c => new SqlServerRetryingExecutionStrategy(c, maxRetryCount, maxRetryDelay, errorNumbersToAdd));

/// <summary>
/// Sets the SQL Server 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 SQL Server. Defaults to <c>150</c> (SQL Server 2019).
/// </summary>
/// <remarks>
/// See <see href="https://aka.ms/efcore-docs-dbcontext-options">Using DbContextOptions</see>, and
/// <see href="https://learn.microsoft.com/sql/t-sql/statements/alter-database-transact-sql-compatibility-level">SQL Server
/// documentation on compatibility level</see> for more information and examples.
/// </remarks>
/// <param name="compatibilityLevel"><see langword="false" /> to have null resource</param>
// TODO: Naming; Cosmos doesn't have Use/Set, so just CompatibilityLevel? SetCompatibilityLevel?
public virtual SqlServerDbContextOptionsBuilder UseCompatibilityLevel(int compatibilityLevel)
=> WithOption(e => e.WithCompatibilityLevel(compatibilityLevel));
}

0 comments on commit e76605e

Please sign in to comment.