Skip to content

Commit 540d01a

Browse files
authored
Fix primitive/complex collection handling on subtypes (10.0) (#37488)
Fixes #37478
1 parent e882d4c commit 540d01a

File tree

4 files changed

+66
-0
lines changed

4 files changed

+66
-0
lines changed

src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,9 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal;
88

99
public partial class NavigationExpandingExpressionVisitor
1010
{
11+
private static readonly bool UseOldBehavior37478 =
12+
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue37478", out var enabled) && enabled;
13+
1114
/// <summary>
1215
/// Expands navigations in the given tree for given source.
1316
/// Optionally also expands navigations for includes.
@@ -123,6 +126,16 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp
123126

124127
if (structuralType is not null)
125128
{
129+
if (!UseOldBehavior37478 && entityReference is not null && convertedType is not null)
130+
{
131+
structuralType = entityReference.EntityType.GetAllBaseTypes().Concat(entityReference.EntityType.GetDerivedTypesInclusive())
132+
.FirstOrDefault(et => et.ClrType == convertedType);
133+
if (structuralType == null)
134+
{
135+
return null;
136+
}
137+
}
138+
126139
var complexProperty = memberIdentity.MemberInfo != null
127140
? structuralType.FindComplexProperty(memberIdentity.MemberInfo)
128141
: memberIdentity.Name is not null

test/EFCore.Cosmos.FunctionalTests/Query/NonSharedPrimitiveCollectionsQueryCosmosTest.cs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,18 @@ FROM root c
362362
""");
363363
}
364364

365+
public override async Task Subquery_over_primitive_collection_on_inheritance_derived_type()
366+
{
367+
await base.Subquery_over_primitive_collection_on_inheritance_derived_type();
368+
369+
AssertSql(
370+
"""
371+
SELECT VALUE c
372+
FROM root c
373+
WHERE ((c["$type"] = "SubType") AND (ARRAY_LENGTH(c["Ints"]) > 0))
374+
""");
375+
}
376+
365377
[ConditionalFact]
366378
public virtual void Check_all_tests_overridden()
367379
=> TestHelpers.AssertAllMethodsOverridden(GetType());

test/EFCore.Specification.Tests/Query/NonSharedPrimitiveCollectionsQueryTestBase.cs

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,6 +256,33 @@ private class Owned
256256
public int Foo { get; set; }
257257
}
258258

259+
[ConditionalFact] // #37478
260+
public virtual async Task Subquery_over_primitive_collection_on_inheritance_derived_type()
261+
{
262+
var contextFactory = await InitializeAsync<TestContext>(
263+
onModelCreating: mb =>
264+
{
265+
mb.Entity<BaseType>();
266+
mb.Entity<SubType>();
267+
});
268+
269+
await using var context = contextFactory.CreateContext();
270+
271+
_ = await context.Set<BaseType>()
272+
.Where(x => ((SubType)x).Ints.Any())
273+
.ToListAsync();
274+
}
275+
276+
public abstract class BaseType
277+
{
278+
public int Id { get; set; }
279+
}
280+
281+
public class SubType : BaseType
282+
{
283+
public required int[] Ints { get; set; }
284+
}
285+
259286
/// <summary>
260287
/// A utility that allows easy testing of querying out arbitrary element types from a primitive collection, provided two distinct
261288
/// element values.

test/EFCore.SqlServer.FunctionalTests/Query/NonSharedPrimitiveCollectionsQuerySqlServerTest.cs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1026,6 +1026,20 @@ WHERE [t].[Id] IN (@ints1, @ints2, @ints3, @ints4, @ints5, @ints6, @ints7, @ints
10261026
""");
10271027
}
10281028

1029+
public override async Task Subquery_over_primitive_collection_on_inheritance_derived_type()
1030+
{
1031+
await base.Subquery_over_primitive_collection_on_inheritance_derived_type();
1032+
1033+
AssertSql(
1034+
"""
1035+
SELECT [b].[Id], [b].[Discriminator], [b].[Ints]
1036+
FROM [BaseType] AS [b]
1037+
WHERE EXISTS (
1038+
SELECT 1
1039+
FROM OPENJSON([b].[Ints]) AS [i])
1040+
""");
1041+
}
1042+
10291043
[ConditionalFact]
10301044
public virtual async Task Same_parameter_with_different_type_mappings()
10311045
{

0 commit comments

Comments
 (0)