diff --git a/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs b/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs
index b797f8a4add..456184ec46a 100644
--- a/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs
+++ b/src/EFCore.Design/Design/Internal/AppServiceProviderFactory.cs
@@ -89,4 +89,30 @@ private IServiceProvider CreateEmptyServiceProvider()
return new ServiceCollection().BuildServiceProvider();
}
+
+ ///
+ /// 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 static void SetEnvironment(IOperationReporter reporter)
+ {
+ var aspnetCoreEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
+ var dotnetEnvironment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
+ var environment = aspnetCoreEnvironment
+ ?? dotnetEnvironment
+ ?? "Development";
+ if (aspnetCoreEnvironment == null)
+ {
+ Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", environment);
+ }
+
+ if (dotnetEnvironment == null)
+ {
+ Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", environment);
+ }
+
+ reporter.WriteVerbose(DesignStrings.UsingEnvironment(environment));
+ }
}
diff --git a/src/EFCore.Design/Design/Internal/DatabaseOperations.cs b/src/EFCore.Design/Design/Internal/DatabaseOperations.cs
index d27a2532b3c..f02f26bf4ed 100644
--- a/src/EFCore.Design/Design/Internal/DatabaseOperations.cs
+++ b/src/EFCore.Design/Design/Internal/DatabaseOperations.cs
@@ -11,6 +11,7 @@ namespace Microsoft.EntityFrameworkCore.Design.Internal;
///
public class DatabaseOperations
{
+ private readonly IOperationReporter _reporter;
private readonly string _projectDir;
private readonly string? _rootNamespace;
private readonly string? _language;
@@ -34,6 +35,7 @@ public DatabaseOperations(
bool nullable,
string[]? args)
{
+ _reporter = reporter;
_projectDir = projectDir;
_rootNamespace = rootNamespace;
_language = language;
@@ -73,6 +75,7 @@ public virtual SavedModelFiles ScaffoldContext(
? Path.GetFullPath(Path.Combine(_projectDir, outputContextDir))
: outputDir;
+ AppServiceProviderFactory.SetEnvironment(_reporter);
var services = _servicesBuilder.Build(provider);
using var scope = services.CreateScope();
diff --git a/src/EFCore.Design/Design/Internal/DbContextOperations.cs b/src/EFCore.Design/Design/Internal/DbContextOperations.cs
index 761579c8bdb..f430f9892b0 100644
--- a/src/EFCore.Design/Design/Internal/DbContextOperations.cs
+++ b/src/EFCore.Design/Design/Internal/DbContextOperations.cs
@@ -503,22 +503,7 @@ private IDictionary> FindContextTypes(string? name = null,
{
_reporter.WriteVerbose(DesignStrings.FindingContexts);
- var aspnetCoreEnvironment = Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT");
- var dotnetEnvironment = Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT");
- var environment = aspnetCoreEnvironment
- ?? dotnetEnvironment
- ?? "Development";
- if (aspnetCoreEnvironment == null)
- {
- Environment.SetEnvironmentVariable("ASPNETCORE_ENVIRONMENT", environment);
- }
-
- if (dotnetEnvironment == null)
- {
- Environment.SetEnvironmentVariable("DOTNET_ENVIRONMENT", environment);
- }
-
- _reporter.WriteVerbose(DesignStrings.UsingEnvironment(environment));
+ AppServiceProviderFactory.SetEnvironment(_reporter);
var contexts = new Dictionary?>();
diff --git a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs
index 3d70a71491f..eb02e70da83 100644
--- a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs
+++ b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs
@@ -109,6 +109,12 @@ public class ExpressionTreeFuncletizer : ExpressionVisitor
private static readonly bool UseOldBehavior35111 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35111", out var enabled35111) && enabled35111;
+ private static readonly bool UseOldBehavior35656 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35656", out var enabled35656) && enabled35656;
+
+ private static readonly bool UseOldBehavior35100 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35100", out var enabled35100) && enabled35100;
+
private static readonly MethodInfo ReadOnlyCollectionIndexerGetter = typeof(ReadOnlyCollection).GetProperties()
.Single(p => p.GetIndexParameters() is { Length: 1 } indexParameters && indexParameters[0].ParameterType == typeof(int)).GetMethod!;
@@ -971,6 +977,51 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall)
}
}
+ // .NET 10 made changes to overload resolution to prefer Span-based overloads when those exist ("first-class spans").
+ // Unfortunately, the LINQ interpreter does not support ref structs, so we rewrite e.g. MemoryExtensions.Contains to
+ // Enumerable.Contains here. See https://github.com/dotnet/runtime/issues/109757.
+ if (method.DeclaringType == typeof(MemoryExtensions) && !UseOldBehavior35100)
+ {
+ switch (method.Name)
+ {
+ case nameof(MemoryExtensions.Contains)
+ when methodCall.Arguments is [var arg0, var arg1] && TryUnwrapSpanImplicitCast(arg0, out var unwrappedArg0):
+ {
+ return Visit(
+ Call(
+ EnumerableMethods.Contains.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]),
+ unwrappedArg0, arg1));
+ }
+
+ case nameof(MemoryExtensions.SequenceEqual)
+ when methodCall.Arguments is [var arg0, var arg1]
+ && TryUnwrapSpanImplicitCast(arg0, out var unwrappedArg0)
+ && TryUnwrapSpanImplicitCast(arg1, out var unwrappedArg1):
+ return Visit(
+ Call(
+ EnumerableMethods.SequenceEqual.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]),
+ unwrappedArg0, unwrappedArg1));
+ }
+
+ static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)
+ {
+ if (expression is MethodCallExpression
+ {
+ Method: { Name: "op_Implicit", DeclaringType: { IsGenericType: true } implicitCastDeclaringType },
+ Arguments: [var unwrapped]
+ }
+ && implicitCastDeclaringType.GetGenericTypeDefinition() is var genericTypeDefinition
+ && (genericTypeDefinition == typeof(Span<>) || genericTypeDefinition == typeof(ReadOnlySpan<>)))
+ {
+ result = unwrapped;
+ return true;
+ }
+
+ result = null;
+ return false;
+ }
+ }
+
// Regular/arbitrary method handling from here on
// First, visit the object and all arguments, saving states as well
@@ -2132,8 +2183,12 @@ static Expression RemoveConvert(Expression expression)
}
}
- private static Expression ConvertIfNeeded(Expression expression, Type type)
- => expression.Type == type ? expression : Convert(expression, type);
+ private Expression ConvertIfNeeded(Expression expression, Type type)
+ => expression.Type == type
+ ? expression
+ : UseOldBehavior35656
+ ? Convert(expression, type)
+ : Visit(Convert(expression, type));
private bool IsGenerallyEvaluatable(Expression expression)
=> _evaluatableExpressionFilter.IsEvaluatableExpression(expression, _model)
diff --git a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
index 427e8c56e94..5d315daa6eb 100644
--- a/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
+++ b/src/Microsoft.Data.Sqlite.Core/SqliteConnection.cs
@@ -23,6 +23,9 @@ namespace Microsoft.Data.Sqlite
/// Async Limitations
public partial class SqliteConnection : DbConnection
{
+ private static readonly bool UseOldBehavior35715 =
+ AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue35715", out var enabled35715) && enabled35715;
+
internal const string MainDatabaseName = "main";
private const int SQLITE_WIN32_DATA_DIRECTORY_TYPE = 1;
@@ -48,6 +51,8 @@ public partial class SqliteConnection : DbConnection
private static readonly StateChangeEventArgs _fromClosedToOpenEventArgs = new StateChangeEventArgs(ConnectionState.Closed, ConnectionState.Open);
private static readonly StateChangeEventArgs _fromOpenToClosedEventArgs = new StateChangeEventArgs(ConnectionState.Open, ConnectionState.Closed);
+ private static string[]? NativeDllSearchDirectories;
+
static SqliteConnection()
{
Type.GetType("SQLitePCL.Batteries_V2, SQLitePCLRaw.batteries_v2")
@@ -624,11 +629,82 @@ public virtual void LoadExtension(string file, string? proc = null)
private void LoadExtensionCore(string file, string? proc)
{
- var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
- if (rc != SQLITE_OK)
+ if (UseOldBehavior35715)
+ {
+ var rc = sqlite3_load_extension(Handle, utf8z.FromString(file), utf8z.FromString(proc), out var errmsg);
+ if (rc != SQLITE_OK)
+ {
+ throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
+ }
+ }
+ else
+ {
+ SqliteException? firstException = null;
+ foreach (var path in GetLoadExtensionPaths(file))
+ {
+ var rc = sqlite3_load_extension(Handle, utf8z.FromString(path), utf8z.FromString(proc), out var errmsg);
+ if (rc == SQLITE_OK)
+ {
+ return;
+ }
+
+ if (firstException == null)
+ {
+ // We store the first exception so that error message looks more obvious if file appears in there
+ firstException = new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
+ }
+ }
+
+ if (firstException != null)
+ {
+ throw firstException;
+ }
+ }
+ }
+
+ private static IEnumerable GetLoadExtensionPaths(string file)
+ {
+ // we always try original input first
+ yield return file;
+
+ string? dirName = Path.GetDirectoryName(file);
+
+ // we don't try to guess directories for user, if they pass a path either absolute or relative - they're on their own
+ if (!string.IsNullOrEmpty(dirName))
{
- throw new SqliteException(Resources.SqliteNativeError(rc, errmsg.utf8_to_string()), rc, rc);
+ yield break;
}
+
+ bool shouldTryAddingLibPrefix = !file.StartsWith("lib", StringComparison.Ordinal) && !RuntimeInformation.IsOSPlatform(OSPlatform.Windows);
+
+ if (shouldTryAddingLibPrefix)
+ {
+ yield return $"lib{file}";
+ }
+
+ NativeDllSearchDirectories ??= GetNativeDllSearchDirectories();
+
+ foreach (string dir in NativeDllSearchDirectories)
+ {
+ yield return Path.Combine(dir, file);
+
+ if (shouldTryAddingLibPrefix)
+ {
+ yield return Path.Combine(dir, $"lib{file}");
+ }
+ }
+ }
+
+ private static string[] GetNativeDllSearchDirectories()
+ {
+ string? searchDirs = AppContext.GetData("NATIVE_DLL_SEARCH_DIRECTORIES") as string;
+
+ if (string.IsNullOrEmpty(searchDirs))
+ {
+ return [];
+ }
+
+ return searchDirs!.Split([ Path.PathSeparator ], StringSplitOptions.RemoveEmptyEntries);
}
///
diff --git a/test/EFCore.Design.Tests/Design/Internal/DatabaseOperationsTest.cs b/test/EFCore.Design.Tests/Design/Internal/DatabaseOperationsTest.cs
index d5c7a100554..d49bac41616 100644
--- a/test/EFCore.Design.Tests/Design/Internal/DatabaseOperationsTest.cs
+++ b/test/EFCore.Design.Tests/Design/Internal/DatabaseOperationsTest.cs
@@ -1,6 +1,8 @@
// 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.Internal;
+
namespace Microsoft.EntityFrameworkCore.Design.Internal;
public class DatabaseOperationsTest
@@ -10,8 +12,71 @@ public void Can_pass_null_args()
{
// Even though newer versions of the tools will pass an empty array
// older versions of the tools can pass null args.
+ CreateOperations(null);
+ }
+
+ [ConditionalFact]
+ public void ScaffoldContext_throws_exceptions_for_invalid_context_name()
+ {
+ ValidateContextNameInReverseEngineerGenerator("Invalid!CSharp*Class&Name");
+ ValidateContextNameInReverseEngineerGenerator("1CSharpClassNameCannotStartWithNumber");
+ ValidateContextNameInReverseEngineerGenerator("volatile");
+ }
+
+ private void ValidateContextNameInReverseEngineerGenerator(string contextName)
+ {
+ var operations = CreateOperations([]);
+
+ Assert.Equal(
+ DesignStrings.ContextClassNotValidCSharpIdentifier(contextName),
+ Assert.Throws(
+ () => operations.ScaffoldContext(
+ "Microsoft.EntityFrameworkCore.SqlServer",
+ "connectionstring",
+ "",
+ "",
+ dbContextClassName: contextName,
+ null,
+ null,
+ "FakeNamespace",
+ contextNamespace: null,
+ useDataAnnotations: false,
+ overwriteFiles: true,
+ useDatabaseNames: false,
+ suppressOnConfiguring: true,
+ noPluralize: false))
+ .Message);
+ }
+
+ [ConditionalFact]
+ [SqlServerConfiguredCondition]
+ public void ScaffoldContext_sets_environment()
+ {
+ var operations = CreateOperations([]);
+ operations.ScaffoldContext(
+ "Microsoft.EntityFrameworkCore.SqlServer",
+ TestEnvironment.DefaultConnection,
+ "",
+ "",
+ dbContextClassName: nameof(TestContext),
+ schemas: ["Empty"],
+ null,
+ null,
+ contextNamespace: null,
+ useDataAnnotations: false,
+ overwriteFiles: true,
+ useDatabaseNames: false,
+ suppressOnConfiguring: true,
+ noPluralize: false);
+
+ Assert.Equal("Development", Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT"));
+ Assert.Equal("Development", Environment.GetEnvironmentVariable("DOTNET_ENVIRONMENT"));
+ }
+
+ private static DatabaseOperations CreateOperations(string[] args)
+ {
var assembly = MockAssembly.Create(typeof(TestContext));
- _ = new TestDatabaseOperations(
+ var operations = new DatabaseOperations(
new TestOperationReporter(),
assembly,
assembly,
@@ -19,7 +84,8 @@ public void Can_pass_null_args()
"RootNamespace",
"C#",
nullable: false,
- args: null);
+ args: args);
+ return operations;
}
public class TestContext : DbContext;
diff --git a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs
index ba445da389c..15f7e341fcf 100644
--- a/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs
+++ b/test/EFCore.Design.Tests/Migrations/Design/CSharpMigrationOperationGeneratorTest.cs
@@ -2453,7 +2453,7 @@ public void InsertDataOperation_required_empty_array()
Assert.Single(o.Columns);
Assert.Equal(1, o.Values.GetLength(0));
Assert.Equal(1, o.Values.GetLength(1));
- Assert.Equal([], (string[])o.Values[0, 0]);
+ Assert.Equal(new string[0], (string[])o.Values[0, 0]);
});
[ConditionalFact]
@@ -2478,7 +2478,7 @@ public void InsertDataOperation_required_empty_array_composite()
Assert.Equal(1, o.Values.GetLength(0));
Assert.Equal(3, o.Values.GetLength(1));
Assert.Null(o.Values[0, 1]);
- Assert.Equal([], (string[])o.Values[0, 2]);
+ Assert.Equal(new string[0], (string[])o.Values[0, 2]);
});
[ConditionalFact]
diff --git a/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs b/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs
deleted file mode 100644
index e73a91f883a..00000000000
--- a/test/EFCore.Design.Tests/Scaffolding/Internal/ReverseEngineeringConfigurationTests.cs
+++ /dev/null
@@ -1,38 +0,0 @@
-// 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.Design.Internal;
-using Microsoft.EntityFrameworkCore.Internal;
-
-namespace Microsoft.EntityFrameworkCore.Scaffolding.Internal;
-
-public class ReverseEngineeringConfigurationTests
-{
- [ConditionalFact]
- public void Throws_exceptions_for_invalid_context_name()
- {
- ValidateContextNameInReverseEngineerGenerator("Invalid!CSharp*Class&Name");
- ValidateContextNameInReverseEngineerGenerator("1CSharpClassNameCannotStartWithNumber");
- ValidateContextNameInReverseEngineerGenerator("volatile");
- }
-
- private void ValidateContextNameInReverseEngineerGenerator(string contextName)
- {
- var assembly = typeof(ReverseEngineeringConfigurationTests).Assembly;
- var reverseEngineer = new DesignTimeServicesBuilder(assembly, assembly, new TestOperationReporter(), [])
- .Build("Microsoft.EntityFrameworkCore.SqlServer")
- .CreateScope()
- .ServiceProvider
- .GetRequiredService();
-
- Assert.Equal(
- DesignStrings.ContextClassNotValidCSharpIdentifier(contextName),
- Assert.Throws(
- () => reverseEngineer.ScaffoldModel(
- "connectionstring",
- new DatabaseModelFactoryOptions(),
- new ModelReverseEngineerOptions(),
- new ModelCodeGenerationOptions { ModelNamespace = "FakeNamespace", ContextName = contextName }))
- .Message);
- }
-}
diff --git a/test/EFCore.Design.Tests/TestUtilities/TestDatabaseOperations.cs b/test/EFCore.Design.Tests/TestUtilities/TestDatabaseOperations.cs
deleted file mode 100644
index adf200ef857..00000000000
--- a/test/EFCore.Design.Tests/TestUtilities/TestDatabaseOperations.cs
+++ /dev/null
@@ -1,16 +0,0 @@
-// 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.Design.Internal;
-
-namespace Microsoft.EntityFrameworkCore.TestUtilities;
-
-public class TestDatabaseOperations(
- IOperationReporter reporter,
- Assembly assembly,
- Assembly startupAssembly,
- string projectDir,
- string rootNamespace,
- string language,
- bool nullable,
- string[] args) : DatabaseOperations(reporter, assembly, startupAssembly, projectDir, rootNamespace, language, nullable, args);
diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
index 93ffe9f0cfb..b65b15d2d0c 100644
--- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
+++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs
@@ -6603,6 +6603,23 @@ public virtual Task Enum_array_contains(bool async)
.Where(w => w.SynergyWith != null && types.Contains(w.SynergyWith.AmmunitionType)));
}
+ [ConditionalTheory] // #35656
+ [MemberData(nameof(IsAsyncData))]
+ public virtual Task Coalesce_with_non_root_evaluatable_Convert(bool async)
+ {
+ MilitaryRank? rank = MilitaryRank.Private;
+
+ // The coalesce is simplified away in the funcletizer (since rank is non-null), but a Convert node is added
+ // to convert from MilitaryRank? (the type of rank) to the type of the coalesce expression (non-nullable
+ // MilitaryRank).
+ // This resulting Convert node isn't evaluatable as root (enum convert), and so the NotEvaluatableAsRootHandler
+ // is invoked.
+ return AssertQuery(
+ async,
+ // ReSharper disable once ConstantNullCoalescingCondition
+ ss => ss.Set().Where(g => (rank ?? g.Rank) == g.Rank));
+ }
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public virtual async Task Client_eval_followed_by_aggregate_operation(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
index cae4816b1ae..9429aab66b8 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/GearsOfWarQuerySqlServerTest.cs
@@ -8162,6 +8162,20 @@ FROM OPENJSON(@__types_0_without_nulls) AS [t]
""");
}
+ public override async Task Coalesce_with_non_root_evaluatable_Convert(bool async)
+ {
+ await base.Coalesce_with_non_root_evaluatable_Convert(async);
+
+ AssertSql(
+ """
+@__rank_0='1' (Nullable = true)
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank]
+FROM [Gears] AS [g]
+WHERE @__rank_0 = [g].[Rank]
+""");
+ }
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task DataLength_function_for_string_parameter(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
index ee461d32d9c..c54d2675c03 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPCGearsOfWarQuerySqlServerTest.cs
@@ -10883,6 +10883,26 @@ FROM OPENJSON(@__types_0_without_nulls) AS [t]
""");
}
+ public override async Task Coalesce_with_non_root_evaluatable_Convert(bool async)
+ {
+ await base.Coalesce_with_non_root_evaluatable_Convert(async);
+
+ AssertSql(
+ """
+@__rank_0='1' (Nullable = true)
+
+SELECT [u].[Nickname], [u].[SquadId], [u].[AssignedCityName], [u].[CityOfBirthName], [u].[FullName], [u].[HasSoulPatch], [u].[LeaderNickname], [u].[LeaderSquadId], [u].[Rank], [u].[Discriminator]
+FROM (
+ SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], N'Gear' AS [Discriminator]
+ FROM [Gears] AS [g]
+ UNION ALL
+ SELECT [o].[Nickname], [o].[SquadId], [o].[AssignedCityName], [o].[CityOfBirthName], [o].[FullName], [o].[HasSoulPatch], [o].[LeaderNickname], [o].[LeaderSquadId], [o].[Rank], N'Officer' AS [Discriminator]
+ FROM [Officers] AS [o]
+) AS [u]
+WHERE @__rank_0 = [u].[Rank]
+""");
+ }
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task DataLength_function_for_string_parameter(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
index 076e6c19d97..d5fa8b8562d 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TPTGearsOfWarQuerySqlServerTest.cs
@@ -9220,6 +9220,23 @@ FROM OPENJSON(@__types_0_without_nulls) AS [t]
""");
}
+ public override async Task Coalesce_with_non_root_evaluatable_Convert(bool async)
+ {
+ await base.Coalesce_with_non_root_evaluatable_Convert(async);
+
+ AssertSql(
+ """
+@__rank_0='1' (Nullable = true)
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], CASE
+ WHEN [o].[Nickname] IS NOT NULL THEN N'Officer'
+END AS [Discriminator]
+FROM [Gears] AS [g]
+LEFT JOIN [Officers] AS [o] ON [g].[Nickname] = [o].[Nickname] AND [g].[SquadId] = [o].[SquadId]
+WHERE @__rank_0 = [g].[Rank]
+""");
+ }
+
[ConditionalTheory]
[MemberData(nameof(IsAsyncData))]
public async Task DataLength_function_for_string_parameter(bool async)
diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
index ca649d80e12..5d82e72fff3 100644
--- a/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
+++ b/test/EFCore.SqlServer.FunctionalTests/Query/TemporalGearsOfWarQuerySqlServerTest.cs
@@ -6987,6 +6987,20 @@ SELECT TOP(1) ~CAST(([g].[Rank] & 2) ^ 2 AS bit) AS [BitwiseTrue], ~CAST(([g].[R
""");
}
+ public override async Task Coalesce_with_non_root_evaluatable_Convert(bool async)
+ {
+ await base.Coalesce_with_non_root_evaluatable_Convert(async);
+
+ AssertSql(
+ """
+@__rank_0='1' (Nullable = true)
+
+SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOfBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[PeriodEnd], [g].[PeriodStart], [g].[Rank]
+FROM [Gears] FOR SYSTEM_TIME AS OF '2010-01-01T00:00:00.0000000' AS [g]
+WHERE @__rank_0 = [g].[Rank]
+""");
+ }
+
public override async Task Comparison_with_value_converted_subclass(bool async)
{
await base.Comparison_with_value_converted_subclass(async);
diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
index 6e69f0b1ce3..77293ffbe86 100644
--- a/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
+++ b/test/EFCore.Sqlite.FunctionalTests/Query/GearsOfWarQuerySqliteTest.cs
@@ -6383,6 +6383,20 @@ LIMIT @__p_0
""");
}
+ public override async Task Coalesce_with_non_root_evaluatable_Convert(bool async)
+ {
+ await base.Coalesce_with_non_root_evaluatable_Convert(async);
+
+ AssertSql(
+ """
+@__rank_0='1' (Nullable = true)
+
+SELECT "g"."Nickname", "g"."SquadId", "g"."AssignedCityName", "g"."CityOfBirthName", "g"."Discriminator", "g"."FullName", "g"."HasSoulPatch", "g"."LeaderNickname", "g"."LeaderSquadId", "g"."Rank"
+FROM "Gears" AS "g"
+WHERE @__rank_0 = "g"."Rank"
+""");
+ }
+
public override async Task Correlated_collections_with_Take(bool async)
{
await base.Correlated_collections_with_Take(async);
diff --git a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs
index 75dd606cd19..b118885ba85 100644
--- a/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs
+++ b/test/EFCore.Tests/Metadata/Internal/EntityTypeTest.BaseType.cs
@@ -677,7 +677,7 @@ public void Navigations_on_base_type_should_be_inherited()
var specialCustomerType = model.AddEntityType(typeof(SpecialCustomer));
Assert.Equal(new[] { "Orders" }, customerType.GetNavigations().Select(p => p.Name).ToArray());
- Assert.Equal([], specialCustomerType.GetNavigations().Select(p => p.Name).ToArray());
+ Assert.Equal(new string[0], specialCustomerType.GetNavigations().Select(p => p.Name).ToArray());
specialCustomerType.BaseType = customerType;
diff --git a/test/EFCore.Tests/Storage/ValueConversion/BytesToStringConverterTest.cs b/test/EFCore.Tests/Storage/ValueConversion/BytesToStringConverterTest.cs
index a29063dc5ab..c1a2694ba2f 100644
--- a/test/EFCore.Tests/Storage/ValueConversion/BytesToStringConverterTest.cs
+++ b/test/EFCore.Tests/Storage/ValueConversion/BytesToStringConverterTest.cs
@@ -23,7 +23,7 @@ public void Can_convert_bytes_to_strings()
var converter = _bytesToStringConverter.ConvertFromProviderExpression.Compile();
Assert.Equal(new byte[] { 83, 112, 196, 177, 110, 204, 136, 97, 108, 32, 84, 97, 112 }, converter("U3DEsW7MiGFsIFRhcA=="));
- Assert.Equal([], converter(""));
+ Assert.Equal(new byte[0], converter(""));
}
[ConditionalFact]
diff --git a/test/EFCore.Tests/Storage/ValueConversion/StringToBytesConverterTest.cs b/test/EFCore.Tests/Storage/ValueConversion/StringToBytesConverterTest.cs
index f2aa07d914c..6842b227c4a 100644
--- a/test/EFCore.Tests/Storage/ValueConversion/StringToBytesConverterTest.cs
+++ b/test/EFCore.Tests/Storage/ValueConversion/StringToBytesConverterTest.cs
@@ -13,7 +13,7 @@ public void Can_convert_strings_to_UTF8()
var converter = _stringToUtf8Converter.ConvertToProviderExpression.Compile();
Assert.Equal(new byte[] { 83, 112, 196, 177, 110, 204, 136, 97, 108, 32, 84, 97, 112 }, converter("Spın̈al Tap"));
- Assert.Equal([], converter(""));
+ Assert.Equal(new byte[0], converter(""));
}
[ConditionalFact]