Skip to content

Commit 89e9446

Browse files
authored
Fix parameter names for nested types in complex type equality (#33527)
Fixes #33449
1 parent 1267c39 commit 89e9446

File tree

7 files changed

+160
-28
lines changed

7 files changed

+160
-28
lines changed

src/EFCore.Relational/Query/RelationalSqlTranslatingExpressionVisitor.cs

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44
using System.Collections;
55
using System.Diagnostics.CodeAnalysis;
6+
using System.Text;
67
using Microsoft.EntityFrameworkCore.Metadata.Internal;
78
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;
89

@@ -2068,11 +2069,18 @@ when sqlParameterExpression.Name.StartsWith(QueryCompilationContext.QueryParamet
20682069
Expression.Constant(property, typeof(IProperty))),
20692070
QueryCompilationContext.QueryContextParameter);
20702071

2071-
var newParameterName =
2072-
$"{RuntimeParameterPrefix}"
2073-
+ $"{chainExpression.ParameterExpression.Name[QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}";
2072+
var parameterNameBuilder = new StringBuilder(RuntimeParameterPrefix)
2073+
.Append(chainExpression.ParameterExpression.Name[QueryCompilationContext.QueryParameterPrefix.Length..])
2074+
.Append('_');
20742075

2075-
return _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda);
2076+
foreach (var complexProperty in chainExpression.ComplexPropertyChain)
2077+
{
2078+
parameterNameBuilder.Append(complexProperty.Name).Append('_');
2079+
}
2080+
2081+
parameterNameBuilder.Append(property.Name);
2082+
2083+
return _queryCompilationContext.RegisterRuntimeParameter(parameterNameBuilder.ToString(), lambda);
20762084
}
20772085

20782086
case MemberInitExpression memberInitExpression

test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ public class InMemoryComplianceTest : ComplianceTestBase
99
{
1010
// No in-memory tests
1111
typeof(ComplexTypeQueryTestBase<>),
12+
typeof(AdHocComplexTypeQueryTestBase),
1213
typeof(PrimitiveCollectionsQueryTestBase<>),
1314
typeof(NonSharedPrimitiveCollectionsQueryTestBase),
1415
typeof(FunkyDataQueryTestBase<>),
Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Query;
5+
6+
// ReSharper disable ClassNeverInstantiated.Local
7+
8+
public abstract class AdHocComplexTypeQueryTestBase : NonSharedModelTestBase
9+
{
10+
#region 33449
11+
12+
[ConditionalFact]
13+
public virtual async Task Complex_type_equals_parameter_with_nested_types_with_property_of_same_name()
14+
{
15+
var contextFactory = await InitializeAsync<Context33449>(
16+
seed: context =>
17+
{
18+
context.AddRange(
19+
new Context33449.EntityType
20+
{
21+
ComplexContainer = new()
22+
{
23+
Id = 1,
24+
Containee1 = new() { Id = 2 },
25+
Containee2 = new() { Id = 3 }
26+
}
27+
});
28+
return context.SaveChangesAsync();
29+
});
30+
31+
await using var context = contextFactory.CreateContext();
32+
33+
var container = new Context33449.ComplexContainer
34+
{
35+
Id = 1,
36+
Containee1 = new() { Id = 2 },
37+
Containee2 = new() { Id = 3 }
38+
};
39+
40+
_ = await context.Set<Context33449.EntityType>().Where(b => b.ComplexContainer == container).SingleAsync();
41+
}
42+
43+
private class Context33449(DbContextOptions options) : DbContext(options)
44+
{
45+
protected override void OnModelCreating(ModelBuilder modelBuilder)
46+
=> modelBuilder.Entity<EntityType>().ComplexProperty(b => b.ComplexContainer, x =>
47+
{
48+
x.ComplexProperty(c => c.Containee1);
49+
x.ComplexProperty(c => c.Containee2);
50+
});
51+
52+
public class EntityType
53+
{
54+
public int Id { get; set; }
55+
public ComplexContainer ComplexContainer { get; set; } = null!;
56+
}
57+
58+
public class ComplexContainer
59+
{
60+
public int Id { get; set; }
61+
62+
public ComplexContainee1 Containee1 { get; set; } = null!;
63+
public ComplexContainee2 Containee2 { get; set; } = null!;
64+
}
65+
66+
public class ComplexContainee1
67+
{
68+
public int Id { get; set; }
69+
}
70+
71+
public class ComplexContainee2
72+
{
73+
public int Id { get; set; }
74+
}
75+
}
76+
77+
#endregion 33449
78+
79+
protected override string StoreName
80+
=> "AdHocComplexTypeQueryTest";
81+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Query;
5+
6+
public class AdHocComplexTypeQuerySqlServerTest : AdHocComplexTypeQueryTestBase
7+
{
8+
public override async Task Complex_type_equals_parameter_with_nested_types_with_property_of_same_name()
9+
{
10+
await base.Complex_type_equals_parameter_with_nested_types_with_property_of_same_name();
11+
12+
AssertSql(
13+
"""
14+
@__entity_equality_container_0_Id='1' (Nullable = true)
15+
@__entity_equality_container_0_Containee1_Id='2' (Nullable = true)
16+
@__entity_equality_container_0_Containee2_Id='3' (Nullable = true)
17+
18+
SELECT TOP(2) [e].[Id], [e].[ComplexContainer_Id], [e].[ComplexContainer_Containee1_Id], [e].[ComplexContainer_Containee2_Id]
19+
FROM [EntityType] AS [e]
20+
WHERE [e].[ComplexContainer_Id] = @__entity_equality_container_0_Id AND [e].[ComplexContainer_Containee1_Id] = @__entity_equality_container_0_Containee1_Id AND [e].[ComplexContainer_Containee2_Id] = @__entity_equality_container_0_Containee2_Id
21+
""");
22+
}
23+
24+
protected TestSqlLoggerFactory TestSqlLoggerFactory
25+
=> (TestSqlLoggerFactory)ListLoggerFactory;
26+
27+
protected void AssertSql(params string[] expected)
28+
=> TestSqlLoggerFactory.AssertBaseline(expected);
29+
30+
protected override ITestStoreFactory TestStoreFactory
31+
=> SqlServerTestStoreFactory.Instance;
32+
}

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

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -236,12 +236,12 @@ public override async Task Complex_type_equals_parameter(bool async)
236236
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
237237
@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 4000)
238238
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
239-
@__entity_equality_address_0_Code='US' (Size = 4000)
240-
@__entity_equality_address_0_FullName='United States' (Size = 4000)
239+
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
240+
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
241241
242242
SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
243243
FROM [Customer] AS [c]
244-
WHERE [c].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c].[ShippingAddress_AddressLine2] IS NULL AND [c].[ShippingAddress_Tags] = @__entity_equality_address_0_Tags AND [c].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [c].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName
244+
WHERE [c].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c].[ShippingAddress_AddressLine2] IS NULL AND [c].[ShippingAddress_Tags] = @__entity_equality_address_0_Tags AND [c].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Country_Code AND [c].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_Country_FullName
245245
""");
246246
}
247247

@@ -268,15 +268,15 @@ public override async Task Contains_over_complex_type(bool async)
268268
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
269269
@__entity_equality_address_0_Tags='["foo","bar"]' (Size = 4000)
270270
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
271-
@__entity_equality_address_0_Code='US' (Size = 4000)
272-
@__entity_equality_address_0_FullName='United States' (Size = 4000)
271+
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
272+
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
273273
274274
SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_Tags], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_Tags], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
275275
FROM [Customer] AS [c]
276276
WHERE EXISTS (
277277
SELECT 1
278278
FROM [Customer] AS [c0]
279-
WHERE [c0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c0].[ShippingAddress_AddressLine2] IS NULL AND [c0].[ShippingAddress_Tags] = @__entity_equality_address_0_Tags AND [c0].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c0].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [c0].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName)
279+
WHERE [c0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c0].[ShippingAddress_AddressLine2] IS NULL AND [c0].[ShippingAddress_Tags] = @__entity_equality_address_0_Tags AND [c0].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [c0].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Country_Code AND [c0].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_Country_FullName)
280280
""");
281281
}
282282

@@ -604,12 +604,12 @@ public override async Task Struct_complex_type_equals_parameter(bool async)
604604
"""
605605
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
606606
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
607-
@__entity_equality_address_0_Code='US' (Size = 4000)
608-
@__entity_equality_address_0_FullName='United States' (Size = 4000)
607+
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
608+
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
609609
610610
SELECT [v].[Id], [v].[Name], [v].[BillingAddress_AddressLine1], [v].[BillingAddress_AddressLine2], [v].[BillingAddress_ZipCode], [v].[BillingAddress_Country_Code], [v].[BillingAddress_Country_FullName], [v].[ShippingAddress_AddressLine1], [v].[ShippingAddress_AddressLine2], [v].[ShippingAddress_ZipCode], [v].[ShippingAddress_Country_Code], [v].[ShippingAddress_Country_FullName]
611611
FROM [ValuedCustomer] AS [v]
612-
WHERE [v].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [v].[ShippingAddress_AddressLine2] IS NULL AND [v].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [v].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [v].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName
612+
WHERE [v].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [v].[ShippingAddress_AddressLine2] IS NULL AND [v].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [v].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Country_Code AND [v].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_Country_FullName
613613
""");
614614
}
615615

@@ -628,15 +628,15 @@ public override async Task Contains_over_struct_complex_type(bool async)
628628
"""
629629
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
630630
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
631-
@__entity_equality_address_0_Code='US' (Size = 4000)
632-
@__entity_equality_address_0_FullName='United States' (Size = 4000)
631+
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
632+
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)
633633
634634
SELECT [v].[Id], [v].[Name], [v].[BillingAddress_AddressLine1], [v].[BillingAddress_AddressLine2], [v].[BillingAddress_ZipCode], [v].[BillingAddress_Country_Code], [v].[BillingAddress_Country_FullName], [v].[ShippingAddress_AddressLine1], [v].[ShippingAddress_AddressLine2], [v].[ShippingAddress_ZipCode], [v].[ShippingAddress_Country_Code], [v].[ShippingAddress_Country_FullName]
635635
FROM [ValuedCustomer] AS [v]
636636
WHERE EXISTS (
637637
SELECT 1
638638
FROM [ValuedCustomer] AS [v0]
639-
WHERE [v0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [v0].[ShippingAddress_AddressLine2] IS NULL AND [v0].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [v0].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Code AND [v0].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_FullName)
639+
WHERE [v0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [v0].[ShippingAddress_AddressLine2] IS NULL AND [v0].[ShippingAddress_ZipCode] = @__entity_equality_address_0_ZipCode AND [v0].[ShippingAddress_Country_Code] = @__entity_equality_address_0_Country_Code AND [v0].[ShippingAddress_Country_FullName] = @__entity_equality_address_0_Country_FullName)
640640
""");
641641
}
642642

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// Licensed to the .NET Foundation under one or more agreements.
2+
// The .NET Foundation licenses this file to you under the MIT license.
3+
4+
namespace Microsoft.EntityFrameworkCore.Query;
5+
6+
public class AdHocComplexTypeQuerySqliteTest : AdHocComplexTypeQueryTestBase
7+
{
8+
protected override ITestStoreFactory TestStoreFactory
9+
=> SqliteTestStoreFactory.Instance;
10+
}

0 commit comments

Comments
 (0)