Skip to content

Commit

Permalink
Query: Match memberInfo in hierarchy for reducing MemberInitExpression
Browse files Browse the repository at this point in the history
Issue: For compiler generated trees the MemberInfos always in sync. But for manually generated trees, if derived type parameter is used then the MemberInfo.ReflectedType is different. Which throws off ReplacingExpressionVisitor when comparing MemberInfo.
Fix: Use helper method IsSameAs which compare memberInfos which refer to same member access in given hierarchy

Resolves #19087
  • Loading branch information
smitpatel committed Dec 5, 2019
1 parent bd263ba commit 4b42c7a
Show file tree
Hide file tree
Showing 4 changed files with 38 additions and 14 deletions.
3 changes: 2 additions & 1 deletion src/EFCore/Query/ReplacingExpressionVisitor.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System.Collections.Generic;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Internal;

Expand Down Expand Up @@ -60,7 +61,7 @@ protected override Expression VisitMember(MemberExpression memberExpression)

if (innerExpression is MemberInitExpression memberInitExpression
&& memberInitExpression.Bindings.SingleOrDefault(
mb => mb.Member == memberExpression.Member) is MemberAssignment memberAssignment)
mb => mb.Member.IsSameAs(memberExpression.Member)) is MemberAssignment memberAssignment)
{
return memberAssignment.Expression;
}
Expand Down
11 changes: 0 additions & 11 deletions src/Shared/MemberInfoExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,16 +31,5 @@ public static string GetSimpleMemberName(this MemberInfo member)
var index = name.LastIndexOf('.');
return index >= 0 ? name.Substring(index + 1) : name;
}

private sealed class MemberInfoComparer : IEqualityComparer<MemberInfo>
{
public static readonly MemberInfoComparer Instance = new MemberInfoComparer();

public bool Equals(MemberInfo x, MemberInfo y)
=> x.IsSameAs(y);

public int GetHashCode(MemberInfo obj)
=> obj.GetHashCode();
}
}
}
27 changes: 25 additions & 2 deletions test/EFCore.Specification.Tests/Query/InheritanceTestBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information.

using System.Linq;
using System.Linq.Expressions;
using Microsoft.EntityFrameworkCore.Infrastructure;
using Microsoft.EntityFrameworkCore.Storage;
using Microsoft.EntityFrameworkCore.TestModels.Inheritance;
Expand Down Expand Up @@ -523,8 +524,6 @@ public virtual void Setting_foreign_key_to_a_different_type_throws()
}
}

protected virtual bool EnforcesFkConstraints => true;

[ConditionalFact]
public virtual void Byte_enum_value_constant_used_in_projection()
{
Expand All @@ -536,8 +535,32 @@ public virtual void Byte_enum_value_constant_used_in_projection()
Assert.Equal(Island.North, result[0]);
}

[ConditionalFact]
public virtual void Member_access_on_intermediate_type_works()
{
using var context = CreateContext();
var query = context.Set<Kiwi>().Select(k => new Kiwi { Name = k.Name });

var parameter = Expression.Parameter(query.ElementType, "p");
var property = Expression.Property(parameter, "Name");
var getProperty = Expression.Lambda(property, new[] { parameter });

var expression = Expression.Call(typeof(Queryable), nameof(Queryable.OrderBy),
new[] { query.ElementType, typeof(string) },
new[] { query.Expression, Expression.Quote(getProperty) });

query = query.Provider.CreateQuery<Kiwi>(expression);

var result = query.ToList();

var kiwi = Assert.Single(result);
Assert.Equal("Great spotted kiwi", kiwi.Name);
}

protected InheritanceContext CreateContext() => Fixture.CreateContext();

protected virtual bool EnforcesFkConstraints => true;

protected virtual void ClearLog()
{
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -500,6 +500,17 @@ FROM [Animal] AS [a0]
WHERE CAST(0 AS bit) = CAST(1 AS bit)");
}

public override void Member_access_on_intermediate_type_works()
{
base.Member_access_on_intermediate_type_works();

AssertSql(
@"SELECT [a].[Name]
FROM [Animal] AS [a]
WHERE [a].[Discriminator] = N'Kiwi'
ORDER BY [a].[Name]");
}

protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction)
=> facade.UseTransaction(transaction.GetDbTransaction());

Expand Down

0 comments on commit 4b42c7a

Please sign in to comment.