From 21865b600336b57fbf8de099257132aa1abcf82d Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Wed, 5 Jun 2019 13:28:13 -0700 Subject: [PATCH] Query: Allow property access on derived type by using type casting Resolves #15807 Resolves #15848 (Since client eval for projection is already added) --- ...lationalSqlTranslatingExpressionVisitor.cs | 73 +++++++++++-------- .../Query/GearsOfWarQueryTestBase.cs | 30 ++++---- 2 files changed, 56 insertions(+), 47 deletions(-) diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs index eeb5ad6434b..8afa0d65f6d 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs @@ -79,17 +79,54 @@ protected override Expression VisitExtension(Expression node) protected override Expression VisitMember(MemberExpression memberExpression) { - var innerExpression = Visit(memberExpression.Expression); - if (innerExpression is EntityShaperExpression entityShaper) + if (memberExpression.Expression is EntityShaperExpression + || (memberExpression.Expression is UnaryExpression innerUnaryExpression + && innerUnaryExpression.NodeType == ExpressionType.Convert + && innerUnaryExpression.Operand is EntityShaperExpression)) { - return BindProperty(entityShaper, entityShaper.EntityType.FindProperty(memberExpression.Member.GetSimpleMemberName())); + return BindProperty(memberExpression.Expression, memberExpression.Member.GetSimpleMemberName()); } + var innerExpression = Visit(memberExpression.Expression); + return TranslationFailed(memberExpression.Expression, innerExpression) ? null : _memberTranslatorProvider.Translate((SqlExpression)innerExpression, memberExpression.Member, memberExpression.Type); } + private SqlExpression BindProperty(Expression source, string propertyName) + { + Type convertedType = null; + if (source is UnaryExpression unaryExpression + && unaryExpression.NodeType == ExpressionType.Convert) + { + source = unaryExpression.Operand; + if (unaryExpression.Type != typeof(object)) + { + convertedType = unaryExpression.Type; + } + } + + if (source is EntityShaperExpression entityShaper) + { + var entityType = entityShaper.EntityType; + if (convertedType != null) + { + entityType = entityType.RootType().GetDerivedTypesInclusive() + .FirstOrDefault(et => et.ClrType == convertedType); + + if (entityType == null) + { + return null; + } + } + + return BindProperty(entityShaper, entityType.FindProperty(propertyName)); + } + + throw new InvalidOperationException(); + } + private SqlExpression BindProperty(EntityShaperExpression entityShaper, IProperty property) { return ((SelectExpression)entityShaper.ValueBufferExpression.QueryExpression) @@ -133,35 +170,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp { if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName)) { - Type convertedType = null; - if (source is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert) - { - source = unaryExpression.Operand; - if (unaryExpression.Type != typeof(object)) - { - convertedType = unaryExpression.Type; - } - } - - if (source is EntityShaperExpression entityShaper) - { - var entityType = entityShaper.EntityType; - if (convertedType != null) - { - entityType = entityType.RootType().GetDerivedTypesInclusive() - .FirstOrDefault(et => et.ClrType == convertedType); - } - - if (entityType != null) - { - var property = entityType.FindProperty(propertyName); - - return BindProperty(entityShaper, property); - } - } - - throw new InvalidOperationException(); + return BindProperty(source, propertyName); } if (methodCallExpression.Method.DeclaringType == typeof(Queryable)) diff --git a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs index fe2d55210fd..83037bd352e 100644 --- a/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/GearsOfWarQueryTestBase.cs @@ -1171,7 +1171,7 @@ from g2 in gs select g1.LeaderNickname != null ? g2.LeaderNickname : (string)null); } - [ConditionalTheory(Skip = "issue #15848")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_null_propagation_negative3(bool isAsync) { @@ -1198,7 +1198,7 @@ orderby Maybe(g2, () => g2.Nickname) assertOrder: true); } - [ConditionalTheory(Skip = "issue #15848")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_null_propagation_negative4(bool isAsync) { @@ -1217,7 +1217,7 @@ orderby Maybe(g2, () => g2.Nickname) assertOrder: true); } - [ConditionalTheory(Skip = "issue #15848")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task Select_null_propagation_negative5(bool isAsync) { @@ -3529,7 +3529,7 @@ private static IEnumerable Veterans(IEnumerable gears) return gears.Where(g => g.Nickname == "Marcus" || g.Nickname == "Dom" || g.Nickname == "Cole Train" || g.Nickname == "Baird"); } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Member_access_on_derived_entity_using_cast() { using (var ctx = CreateContext()) @@ -3553,7 +3553,7 @@ where f is LocustHorde } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Member_access_on_derived_materialized_entity_using_cast() { using (var ctx = CreateContext()) @@ -3577,7 +3577,7 @@ orderby f.Name } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Member_access_on_derived_entity_using_cast_and_let() { using (var ctx = CreateContext()) @@ -4067,7 +4067,7 @@ public virtual void Optional_navigation_with_collection_composite_key() } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Select_null_conditional_with_inheritance() { using (var context = CreateContext()) @@ -4083,7 +4083,7 @@ public virtual void Select_null_conditional_with_inheritance() } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Select_null_conditional_with_inheritance_negative() { using (var context = CreateContext()) @@ -5939,7 +5939,7 @@ join g in gs on o.FullName equals g.FullName into grouping elementAsserter: (e, a) => CollectionAsserter(elementSorter: ee => ee)(e.Collection, a.Collection)); } - [ConditionalTheory(Skip = "issue #15848")] + [ConditionalTheory(Skip = "Issue#15611")] [MemberData(nameof(IsAsyncData))] public virtual Task Outer_parameter_in_group_join_with_DefaultIfEmpty(bool isAsync) { @@ -6025,7 +6025,7 @@ public virtual Task Order_by_entity_qsre(bool isAsync) assertOrder: true); } - [ConditionalTheory(Skip = "issue #15848")] + [ConditionalTheory(Skip = "Issue#15939")] [MemberData(nameof(IsAsyncData))] public virtual Task Order_by_entity_qsre_with_inheritance(bool isAsync) { @@ -7430,7 +7430,7 @@ public virtual void OfTypeNav3() } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact(Skip = "Issue#15964")] public virtual void Nav_rewrite_Distinct_with_convert() { using (var ctx = CreateContext()) @@ -7440,7 +7440,7 @@ public virtual void Nav_rewrite_Distinct_with_convert() } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact(Skip = "Issue#15964")] public virtual void Nav_rewrite_Distinct_with_convert_anonymous() { using (var ctx = CreateContext()) @@ -7460,7 +7460,7 @@ public virtual void Nav_rewrite_with_convert1() } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Nav_rewrite_with_convert2() { using (var ctx = CreateContext()) @@ -7470,7 +7470,7 @@ public virtual void Nav_rewrite_with_convert2() } } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Nav_rewrite_with_convert3() { using (var ctx = CreateContext()) @@ -7489,7 +7489,7 @@ public virtual Task Where_contains_on_navigation_with_composite_keys(bool isAsyn (gs, cs) => gs.Where(g => cs.Any(c => c.BornGears.Contains(g)))); } - [ConditionalFact(Skip = "issue #15848")] + [ConditionalFact] public virtual void Project_derivied_entity_with_convert_to_parent() { using (var ctx = CreateContext())