Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[release/8.0] Fix parameter names for nested types in complex type equality (#33527) #33548

Merged
merged 2 commits into from May 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
Expand Up @@ -3,6 +3,7 @@

using System.Collections;
using System.Diagnostics.CodeAnalysis;
using System.Text;
using Microsoft.EntityFrameworkCore.Metadata.Internal;
using Microsoft.EntityFrameworkCore.Query.SqlExpressions;

Expand Down Expand Up @@ -60,6 +61,9 @@ public class RelationalSqlTranslatingExpressionVisitor : ExpressionVisitor

private bool _throwForNotTranslatedEfProperty;

private static readonly bool UseOldBehavior33449 =
AppContext.TryGetSwitch("Microsoft.EntityFrameworkCore.Issue33449", out var enabled33449) && enabled33449;

/// <summary>
/// Creates a new instance of the <see cref="RelationalSqlTranslatingExpressionVisitor" /> class.
/// </summary>
Expand Down Expand Up @@ -2045,11 +2049,29 @@ private Expression CreatePropertyAccessExpression(Expression target, IProperty p
Expression.Constant(property, typeof(IProperty))),
QueryCompilationContext.QueryContextParameter);

var newParameterName =
$"{RuntimeParameterPrefix}"
+ $"{chainExpression.ParameterExpression.Name[QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}";
if (UseOldBehavior33449)
{
var newParameterName =
$"{RuntimeParameterPrefix}"
+ $"{chainExpression.ParameterExpression.Name[QueryCompilationContext.QueryParameterPrefix.Length..]}_{property.Name}";

return _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda);
return _queryCompilationContext.RegisterRuntimeParameter(newParameterName, lambda);
}
else
{
var parameterNameBuilder = new StringBuilder(RuntimeParameterPrefix)
.Append(chainExpression.ParameterExpression.Name[QueryCompilationContext.QueryParameterPrefix.Length..])
.Append('_');

foreach (var complexProperty in chainExpression.ComplexPropertyChain)
{
parameterNameBuilder.Append(complexProperty.Name).Append('_');
}

parameterNameBuilder.Append(property.Name);

return _queryCompilationContext.RegisterRuntimeParameter(parameterNameBuilder.ToString(), lambda);
}
}

case MemberInitExpression memberInitExpression
Expand Down
Expand Up @@ -9,6 +9,7 @@ public class InMemoryComplianceTest : ComplianceTestBase
{
// No in-memory tests
typeof(ComplexTypeQueryTestBase<>),
typeof(AdHocComplexTypeQueryTestBase),
typeof(PrimitiveCollectionsQueryTestBase<>),
typeof(NonSharedPrimitiveCollectionsQueryTestBase),
typeof(FunkyDataQueryTestBase<>),
Expand Down
@@ -0,0 +1,82 @@
// 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.Query;

// ReSharper disable ClassNeverInstantiated.Local

public abstract class AdHocComplexTypeQueryTestBase : NonSharedModelTestBase
{
#region 33449

[ConditionalFact]
public virtual async Task Complex_type_equals_parameter_with_nested_types_with_property_of_same_name()
{
var contextFactory = await InitializeAsync<Context33449>(
seed: context =>
{
context.AddRange(
new Context33449.EntityType
{
ComplexContainer = new()
{
Id = 1,
Containee1 = new() { Id = 2 },
Containee2 = new() { Id = 3 }
}
});
context.SaveChanges();
});

await using var context = contextFactory.CreateContext();

var container = new Context33449.ComplexContainer
{
Id = 1,
Containee1 = new() { Id = 2 },
Containee2 = new() { Id = 3 }
};

_ = await context.Set<Context33449.EntityType>().Where(b => b.ComplexContainer == container).SingleAsync();
}

private class Context33449(DbContextOptions options) : DbContext(options)
{
protected override void OnModelCreating(ModelBuilder modelBuilder)
=> modelBuilder.Entity<EntityType>().ComplexProperty(b => b.ComplexContainer, x =>
{
x.IsRequired();
x.ComplexProperty(c => c.Containee1).IsRequired();
x.ComplexProperty(c => c.Containee2).IsRequired();
});

public class EntityType
{
public int Id { get; set; }
public ComplexContainer ComplexContainer { get; set; } = null!;
}

public class ComplexContainer
{
public int Id { get; set; }

public ComplexContainee1 Containee1 { get; set; } = null!;
public ComplexContainee2 Containee2 { get; set; } = null!;
}

public class ComplexContainee1
{
public int Id { get; set; }
}

public class ComplexContainee2
{
public int Id { get; set; }
}
}

#endregion 33449

protected override string StoreName
=> "AdHocComplexTypeQueryTest";
}
@@ -0,0 +1,32 @@
// 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.Query;

public class AdHocComplexTypeQuerySqlServerTest : AdHocComplexTypeQueryTestBase
{
public override async Task Complex_type_equals_parameter_with_nested_types_with_property_of_same_name()
{
await base.Complex_type_equals_parameter_with_nested_types_with_property_of_same_name();

AssertSql(
"""
@__entity_equality_container_0_Id='1' (Nullable = true)
@__entity_equality_container_0_Containee1_Id='2' (Nullable = true)
@__entity_equality_container_0_Containee2_Id='3' (Nullable = true)

SELECT TOP(2) [e].[Id], [e].[ComplexContainer_Id], [e].[ComplexContainer_Containee1_Id], [e].[ComplexContainer_Containee2_Id]
FROM [EntityType] AS [e]
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
""");
}

protected TestSqlLoggerFactory TestSqlLoggerFactory
=> (TestSqlLoggerFactory)ListLoggerFactory;

protected void AssertSql(params string[] expected)
=> TestSqlLoggerFactory.AssertBaseline(expected);

protected override ITestStoreFactory TestStoreFactory
=> SqlServerTestStoreFactory.Instance;
}
Expand Up @@ -233,12 +233,12 @@ public override async Task Complex_type_equals_parameter(bool async)
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 4000)
@__entity_equality_address_0_FullName='United States' (Size = 4000)
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)

SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE [c].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c].[ShippingAddress_AddressLine2] IS NULL 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
WHERE [c].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c].[ShippingAddress_AddressLine2] IS NULL 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
""");
}

Expand All @@ -264,15 +264,15 @@ public override async Task Contains_over_complex_type(bool async)
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 4000)
@__entity_equality_address_0_FullName='United States' (Size = 4000)
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)

SELECT [c].[Id], [c].[Name], [c].[BillingAddress_AddressLine1], [c].[BillingAddress_AddressLine2], [c].[BillingAddress_ZipCode], [c].[BillingAddress_Country_Code], [c].[BillingAddress_Country_FullName], [c].[ShippingAddress_AddressLine1], [c].[ShippingAddress_AddressLine2], [c].[ShippingAddress_ZipCode], [c].[ShippingAddress_Country_Code], [c].[ShippingAddress_Country_FullName]
FROM [Customer] AS [c]
WHERE EXISTS (
SELECT 1
FROM [Customer] AS [c0]
WHERE [c0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c0].[ShippingAddress_AddressLine2] IS NULL 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)
WHERE [c0].[ShippingAddress_AddressLine1] = @__entity_equality_address_0_AddressLine1 AND [c0].[ShippingAddress_AddressLine2] IS NULL 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)
""");
}

Expand Down Expand Up @@ -600,12 +600,12 @@ public override async Task Struct_complex_type_equals_parameter(bool async)
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 4000)
@__entity_equality_address_0_FullName='United States' (Size = 4000)
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)

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]
FROM [ValuedCustomer] AS [v]
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
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
""");
}

Expand All @@ -624,15 +624,15 @@ public override async Task Contains_over_struct_complex_type(bool async)
"""
@__entity_equality_address_0_AddressLine1='804 S. Lakeshore Road' (Size = 4000)
@__entity_equality_address_0_ZipCode='38654' (Nullable = true)
@__entity_equality_address_0_Code='US' (Size = 4000)
@__entity_equality_address_0_FullName='United States' (Size = 4000)
@__entity_equality_address_0_Country_Code='US' (Size = 4000)
@__entity_equality_address_0_Country_FullName='United States' (Size = 4000)

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]
FROM [ValuedCustomer] AS [v]
WHERE EXISTS (
SELECT 1
FROM [ValuedCustomer] AS [v0]
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)
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)
""");
}

Expand Down
@@ -0,0 +1,10 @@
// 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.Query;

public class AdHocComplexTypeQuerySqliteTest : AdHocComplexTypeQueryTestBase
{
protected override ITestStoreFactory TestStoreFactory
=> SqliteTestStoreFactory.Instance;
}