From a6979f03b60fd6cac0c658bb556c826dd6fd45b4 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 20:54:15 +0000 Subject: [PATCH 1/4] Initial plan From e7c37f115622eed89118ae0d74f6c42f84a6fbed Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 21:12:47 +0000 Subject: [PATCH 2/4] Handle new Min/Max Span overloads from MemoryExtensions in .NET 11 Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- .../Internal/ExpressionTreeFuncletizer.cs | 17 +++++++++++ .../PrimitiveCollectionsQueryCosmosTest.cs | 30 +++++++++++++++++++ .../PrimitiveCollectionsQueryTestBase.cs | 18 +++++++++++ ...imitiveCollectionsQueryOldSqlServerTest.cs | 30 +++++++++++++++++++ ...imitiveCollectionsQuerySqlServer160Test.cs | 26 ++++++++++++++++ ...veCollectionsQuerySqlServerJsonTypeTest.cs | 26 ++++++++++++++++ .../PrimitiveCollectionsQuerySqlServerTest.cs | 30 +++++++++++++++++++ .../PrimitiveCollectionsQuerySqliteTest.cs | 26 ++++++++++++++++ 8 files changed, 203 insertions(+) diff --git a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs index 4c07cc883c8..f1875019f79 100644 --- a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs +++ b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs @@ -1003,6 +1003,23 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) Call( EnumerableMethods.SequenceEqual.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]), unwrappedSpanArg, unwrappedOtherArg)); + + // .NET 11 added Min/Max Span overloads; rewrite to Enumerable.Min/Max. + case "Min" + when methodCall.Arguments is [var spanArg] + && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg): + return Visit( + Call( + EnumerableMethods.MinWithoutSelector.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]), + unwrappedSpanArg)); + + case "Max" + when methodCall.Arguments is [var spanArg] + && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg): + return Visit( + Call( + EnumerableMethods.MaxWithoutSelector.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]), + unwrappedSpanArg)); } static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result) diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs index 25bc5e5f408..a607ae58f4a 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/PrimitiveCollectionsQueryCosmosTest.cs @@ -2076,6 +2076,36 @@ FROM root c """); } +#if NET11_0_OR_GREATER + public override async Task Min_on_MemoryExtensions() + { + await base.Min_on_MemoryExtensions(); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (( + SELECT VALUE MIN(a) + FROM a IN (SELECT VALUE [30, c["Int"]])) = 30) +"""); + } + + public override async Task Max_on_MemoryExtensions() + { + await base.Max_on_MemoryExtensions(); + + AssertSql( + """ +SELECT VALUE c +FROM root c +WHERE (( + SELECT VALUE MAX(a) + FROM a IN (SELECT VALUE [30, c["Int"]])) = 30) +"""); + } +#endif + [ConditionalFact] public virtual void Check_all_tests_overridden() => TestHelpers.AssertAllMethodsOverridden(GetType()); diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index 00bc11d980d..5fcab017277 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -955,6 +955,24 @@ public virtual Task Contains_with_MemoryExtensions_with_null_comparer() => AssertQuery( ss => ss.Set().Where(c => MemoryExtensions.Contains(new[] { 10, 999 }, c.Int, comparer: null))); +#if NET11_0_OR_GREATER + // .NET 11 first-class spans caused MemoryExtensions.Min to get resolved instead of Enumerable.Min. + // The following tests that the various overloads are all supported. + [ConditionalFact] + [MemberData(nameof(IsAsyncData))] + public virtual Task Min_on_MemoryExtensions() + => AssertQuery( + ss => ss.Set().Where(c => MemoryExtensions.Min(new[] { 30, c.Int }) == 30)); + + // .NET 11 first-class spans caused MemoryExtensions.Max to get resolved instead of Enumerable.Max. + // The following tests that the various overloads are all supported. + [ConditionalFact] + [MemberData(nameof(IsAsyncData))] + public virtual Task Max_on_MemoryExtensions() + => AssertQuery( + ss => ss.Set().Where(c => MemoryExtensions.Max(new[] { 30, c.Int }) == 30)); +#endif + [ConditionalFact] public virtual Task Column_collection_Count_method() => AssertQuery( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs index 9ba60047cb2..33645bccece 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQueryOldSqlServerTest.cs @@ -1010,6 +1010,36 @@ WHERE [p].[Int] IN (10, 999) """); } +#if NET11_0_OR_GREATER + public override async Task Min_on_MemoryExtensions() + { + await base.Min_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT MIN([v].[Value]) + FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30 +"""); + } + + public override async Task Max_on_MemoryExtensions() + { + await base.Max_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT MAX([v].[Value]) + FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30 +"""); + } +#endif + public override Task Column_collection_Count_method() => AssertCompatibilityLevelTooLow(() => base.Column_collection_Count_method()); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs index 102e3bb2e54..aa3919017b7 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServer160Test.cs @@ -1096,6 +1096,32 @@ WHERE [p].[Int] IN (10, 999) """); } +#if NET11_0_OR_GREATER + public override async Task Min_on_MemoryExtensions() + { + await base.Min_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int]) = 30 +"""); + } + + public override async Task Max_on_MemoryExtensions() + { + await base.Max_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int]) = 30 +"""); + } +#endif + public override async Task Column_collection_Count_method() { await base.Column_collection_Count_method(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs index 2f652bd5664..a1a162f1b63 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerJsonTypeTest.cs @@ -1268,6 +1268,32 @@ WHERE [p].[Int] IN (10, 999) """); } +#if NET11_0_OR_GREATER + public override async Task Min_on_MemoryExtensions() + { + await base.Min_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE LEAST(30, [p].[Int]) = 30 +"""); + } + + public override async Task Max_on_MemoryExtensions() + { + await base.Max_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE GREATEST(30, [p].[Int]) = 30 +"""); + } +#endif + public override async Task Column_collection_Count_method() { await base.Column_collection_Count_method(); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs index 68246b08e36..bd85ed66fa0 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/PrimitiveCollectionsQuerySqlServerTest.cs @@ -1118,6 +1118,36 @@ WHERE [p].[Int] IN (10, 999) """); } +#if NET11_0_OR_GREATER + public override async Task Min_on_MemoryExtensions() + { + await base.Min_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT MIN([v].[Value]) + FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30 +"""); + } + + public override async Task Max_on_MemoryExtensions() + { + await base.Max_on_MemoryExtensions(); + + AssertSql( + """ +SELECT [p].[Id], [p].[Bool], [p].[Bools], [p].[DateTime], [p].[DateTimes], [p].[Enum], [p].[Enums], [p].[Int], [p].[Ints], [p].[NullableInt], [p].[NullableInts], [p].[NullableString], [p].[NullableStrings], [p].[NullableWrappedId], [p].[NullableWrappedIdWithNullableComparer], [p].[String], [p].[Strings], [p].[WrappedId] +FROM [PrimitiveCollectionsEntity] AS [p] +WHERE ( + SELECT MAX([v].[Value]) + FROM (VALUES (CAST(30 AS int)), ([p].[Int])) AS [v]([Value])) = 30 +"""); + } +#endif + public override async Task Column_collection_Count_method() { await base.Column_collection_Count_method(); diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs index 844d0975b3e..6fd37f1c338 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/PrimitiveCollectionsQuerySqliteTest.cs @@ -1048,6 +1048,32 @@ public override async Task Contains_with_MemoryExtensions_with_null_comparer() """); } +#if NET11_0_OR_GREATER + public override async Task Min_on_MemoryExtensions() + { + await base.Min_on_MemoryExtensions(); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE min(30, "p"."Int") = 30 +"""); + } + + public override async Task Max_on_MemoryExtensions() + { + await base.Max_on_MemoryExtensions(); + + AssertSql( + """ +SELECT "p"."Id", "p"."Bool", "p"."Bools", "p"."DateTime", "p"."DateTimes", "p"."Enum", "p"."Enums", "p"."Int", "p"."Ints", "p"."NullableInt", "p"."NullableInts", "p"."NullableString", "p"."NullableStrings", "p"."NullableWrappedId", "p"."NullableWrappedIdWithNullableComparer", "p"."String", "p"."Strings", "p"."WrappedId" +FROM "PrimitiveCollectionsEntity" AS "p" +WHERE max(30, "p"."Int") = 30 +"""); + } +#endif + public override async Task Column_collection_Count_method() { await base.Column_collection_Count_method(); From 2ebf719253ee3ae7695c4d53df49c087ae50ad0c Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 13 Jul 2026 21:16:41 +0000 Subject: [PATCH 3/4] Remove MemberData from ConditionalFact test methods Co-authored-by: AndriySvyryd <6539701+AndriySvyryd@users.noreply.github.com> --- .../Query/PrimitiveCollectionsQueryTestBase.cs | 2 -- 1 file changed, 2 deletions(-) diff --git a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs index 5fcab017277..60745cde1c2 100644 --- a/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/PrimitiveCollectionsQueryTestBase.cs @@ -959,7 +959,6 @@ public virtual Task Contains_with_MemoryExtensions_with_null_comparer() // .NET 11 first-class spans caused MemoryExtensions.Min to get resolved instead of Enumerable.Min. // The following tests that the various overloads are all supported. [ConditionalFact] - [MemberData(nameof(IsAsyncData))] public virtual Task Min_on_MemoryExtensions() => AssertQuery( ss => ss.Set().Where(c => MemoryExtensions.Min(new[] { 30, c.Int }) == 30)); @@ -967,7 +966,6 @@ public virtual Task Min_on_MemoryExtensions() // .NET 11 first-class spans caused MemoryExtensions.Max to get resolved instead of Enumerable.Max. // The following tests that the various overloads are all supported. [ConditionalFact] - [MemberData(nameof(IsAsyncData))] public virtual Task Max_on_MemoryExtensions() => AssertQuery( ss => ss.Set().Where(c => MemoryExtensions.Max(new[] { 30, c.Int }) == 30)); From f3f0a99fd190dc405894bfbe9966885c9c03c583 Mon Sep 17 00:00:00 2001 From: Andriy Svyryd Date: Mon, 13 Jul 2026 17:45:34 -0700 Subject: [PATCH 4/4] Potential fix for pull request finding Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com> --- .../Internal/ExpressionTreeFuncletizer.cs | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs index f1875019f79..9e75ade3516 100644 --- a/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs +++ b/src/EFCore/Query/Internal/ExpressionTreeFuncletizer.cs @@ -1008,18 +1008,32 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCall) case "Min" when methodCall.Arguments is [var spanArg] && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg): - return Visit( - Call( - EnumerableMethods.MinWithoutSelector.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]), - unwrappedSpanArg)); + { + var elementType = methodCall.Method.ReturnType; + var enumerableMin = EnumerableMethods.GetMinWithoutSelector(elementType); + + if (enumerableMin.IsGenericMethodDefinition) + { + enumerableMin = enumerableMin.MakeGenericMethod(elementType); + } + + return Visit(Call(enumerableMin, unwrappedSpanArg)); + } case "Max" when methodCall.Arguments is [var spanArg] && TryUnwrapSpanImplicitCast(spanArg, out var unwrappedSpanArg): - return Visit( - Call( - EnumerableMethods.MaxWithoutSelector.MakeGenericMethod(methodCall.Method.GetGenericArguments()[0]), - unwrappedSpanArg)); + { + var elementType = methodCall.Method.ReturnType; + var enumerableMax = EnumerableMethods.GetMaxWithoutSelector(elementType); + + if (enumerableMax.IsGenericMethodDefinition) + { + enumerableMax = enumerableMax.MakeGenericMethod(elementType); + } + + return Visit(Call(enumerableMax, unwrappedSpanArg)); + } } static bool TryUnwrapSpanImplicitCast(Expression expression, [NotNullWhen(true)] out Expression? result)