From feed50c5bf8736abdb4f152a1f3c409e1c131684 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Sat, 25 May 2019 16:56:17 -0700 Subject: [PATCH] Query: Implement inheritance support for relational layer - Add discriminator predicate in SelectExpression (filed #15808 to remove redundant) - Add TPH support in materializer. This happens at core level now - Add translation for is operator inside lambda - Add translation for OfType method --- .../Pipeline/InMemoryShapedQueryExpression.cs | 3 +- ...ationalEntityQueryableExpressionVisitor.cs | 2 +- .../Pipeline/EntityProjectionExpression.cs | 9 +- .../Query/Pipeline/ISqlExpressionFactory.cs | 6 + ...tionalEntityQueryableExpressionVisitor2.cs | 39 +++- ...ntityQueryableExpressionVisitorFactory2.cs | 6 +- ...tionalEntityQueryableExpressionVisitors.cs | 6 +- ...yableMethodTranslatingExpressionVisitor.cs | 55 +++++- ...alShapedQueryCompilingExpressionVisitor.cs | 27 +-- .../RelationalShapedQueryExpression.cs | 28 +-- ...lationalSqlTranslatingExpressionVisitor.cs | 73 ++++++-- .../Query/Pipeline/SqlExpressionFactory.cs | 68 +++++++ .../SqlExpressions/FromSqlExpression.cs | 3 + .../SqlExpressions/SelectExpression.cs | 43 ++--- .../Pipeline/ProjectionBindingExpression.cs | 40 +++- .../Pipeline/ShapedQueryExpressionVisitor.cs | 174 ++++++++++++------ .../FindInMemoryTest.cs | 6 +- .../InMemoryComplianceTest.cs | 1 + .../Query/InheritanceRelationalTestBase.cs | 4 +- .../DatabindingTestBase.cs | 2 +- .../FindTestBase.cs | 37 ++-- .../Query/InheritanceTestBase.cs | 2 +- ...eritanceRelationshipsQuerySqlServerTest.cs | 3 +- .../Query/InheritanceSqlServerTest.cs | 3 +- .../SqlServerComplianceTest.cs | 2 - ...InheritanceRelationshipsQuerySqliteTest.cs | 3 +- .../Query/InheritanceSqliteTest.cs | 3 +- .../SqliteComplianceTest.cs | 2 - 28 files changed, 458 insertions(+), 192 deletions(-) diff --git a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpression.cs b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpression.cs index 9addc6e04c9..207eee72d51 100644 --- a/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpression.cs +++ b/src/EFCore.InMemory/Query/Pipeline/InMemoryShapedQueryExpression.cs @@ -16,7 +16,8 @@ public InMemoryShapedQueryExpression(IEntityType entityType) entityType, new ProjectionBindingExpression( QueryExpression, - new ProjectionMember(), typeof(ValueBuffer)), + new ProjectionMember(), + typeof(ValueBuffer)), false); } } diff --git a/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs b/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs index a76e50fac0a..42f2ddd8c34 100644 --- a/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/ExpressionVisitors/RelationalEntityQueryableExpressionVisitor.cs @@ -441,7 +441,7 @@ var discriminatorPredicate private static Expression IsNotNull(IProperty property, SelectExpression selectExpression, IQuerySource querySource) { - var column = selectExpression.BindProperty(property, querySource); + var column = selectExpression.BindProperty(property, querySource); var nullableColumn = column.Type.IsNullableType() ? column : new NullableExpression(column); return Expression.Not(new IsNullExpression(nullableColumn)); } diff --git a/src/EFCore.Relational/Query/Pipeline/EntityProjectionExpression.cs b/src/EFCore.Relational/Query/Pipeline/EntityProjectionExpression.cs index 0c1b03c9f8c..f4c40246426 100644 --- a/src/EFCore.Relational/Query/Pipeline/EntityProjectionExpression.cs +++ b/src/EFCore.Relational/Query/Pipeline/EntityProjectionExpression.cs @@ -17,14 +17,14 @@ private readonly IDictionary _propertyExpressionsCa public EntityProjectionExpression(IEntityType entityType, TableExpressionBase innerTable, bool nullable) { - EntityType = entityType; + EntityType = entityType.RootType(); _innerTable = innerTable; Nullable = nullable; } public EntityProjectionExpression(IEntityType entityType, IDictionary propertyExpressions) { - EntityType = entityType; + EntityType = entityType.RootType(); _propertyExpressionsCache = propertyExpressions; } @@ -81,6 +81,11 @@ public EntityProjectionExpression MakeNullable() public ColumnExpression GetProperty(IProperty property) { + if (property.DeclaringEntityType.RootType() != EntityType) + { + throw new InvalidOperationException("Called EntityProjectionExpression.GetProperty() with incorrect IProperty"); + } + if (!_propertyExpressionsCache.TryGetValue(property, out var expression)) { expression = new ColumnExpression(property, _innerTable, Nullable); diff --git a/src/EFCore.Relational/Query/Pipeline/ISqlExpressionFactory.cs b/src/EFCore.Relational/Query/Pipeline/ISqlExpressionFactory.cs index 58026f50bbf..c2d47479730 100644 --- a/src/EFCore.Relational/Query/Pipeline/ISqlExpressionFactory.cs +++ b/src/EFCore.Relational/Query/Pipeline/ISqlExpressionFactory.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; @@ -76,6 +77,11 @@ SqlFunctionExpression Function( LikeExpression Like(SqlExpression match, SqlExpression pattern, SqlExpression escapeChar = null); SqlConstantExpression Constant(object value, RelationalTypeMapping typeMapping = null); SqlFragmentExpression Fragment(string sql); + + SelectExpression Select(SqlExpression projection); + SelectExpression Select(IEntityType entityType); + SelectExpression Select(IEntityType entityType, string sql, Expression sqlArguments); + #endregion } } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitor2.cs b/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitor2.cs index d7e0aed913d..c271707a0fc 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitor2.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitor2.cs @@ -4,20 +4,22 @@ using System; using System.Linq; using System.Linq.Expressions; -using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Pipeline; -using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; +using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline { public class RelationalEntityQueryableExpressionVisitor2 : EntityQueryableExpressionVisitor2 { private readonly IModel _model; + private readonly ISqlExpressionFactory _sqlExpressionFactory; - public RelationalEntityQueryableExpressionVisitor2(IModel model) + public RelationalEntityQueryableExpressionVisitor2(IModel model, ISqlExpressionFactory sqlExpressionFactory) { _model = model; + _sqlExpressionFactory = sqlExpressionFactory; } protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) @@ -28,16 +30,39 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp // TODO: Implement parameters var sql = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value; var queryable = (IQueryable)((ConstantExpression)methodCallExpression.Arguments[0]).Value; - return CreateShapedQueryExpression(queryable.ElementType, sql); + return CreateShapedQueryExpression(queryable.ElementType, sql, methodCallExpression.Arguments[2]); } return base.VisitMethodCall(methodCallExpression); } protected override ShapedQueryExpression CreateShapedQueryExpression(Type elementType) - => new RelationalShapedQueryExpression(_model.FindEntityType(elementType)); + { + var entityType = _model.FindEntityType(elementType); + var queryExpression = _sqlExpressionFactory.Select(entityType); + + return CreateShapedQueryExpression(entityType, queryExpression); + } + + protected virtual ShapedQueryExpression CreateShapedQueryExpression(Type elementType, string sql, Expression arguments) + { + var entityType = _model.FindEntityType(elementType); + var queryExpression = _sqlExpressionFactory.Select(entityType, sql, arguments); - protected virtual ShapedQueryExpression CreateShapedQueryExpression(Type elementType, string sql) - => new RelationalShapedQueryExpression(_model.FindEntityType(elementType), sql); + return CreateShapedQueryExpression(entityType, queryExpression); + } + + private ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType, SelectExpression selectExpression) + { + return new RelationalShapedQueryExpression( + selectExpression, + new EntityShaperExpression( + entityType, + new ProjectionBindingExpression( + selectExpression, + new ProjectionMember(), + typeof(ValueBuffer)), + false)); + } } } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitorFactory2.cs b/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitorFactory2.cs index bd2a5d72e5d..0b430035528 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitorFactory2.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitorFactory2.cs @@ -9,15 +9,17 @@ namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline public class RelationalEntityQueryableTranslatorFactory : EntityQueryableTranslatorFactory { private readonly IModel _model; + private readonly ISqlExpressionFactory _sqlExpressionFactory; - public RelationalEntityQueryableTranslatorFactory(IModel model) + public RelationalEntityQueryableTranslatorFactory(IModel model, ISqlExpressionFactory sqlExpressionFactory) { _model = model; + _sqlExpressionFactory = sqlExpressionFactory; } public override EntityQueryableTranslator Create(QueryCompilationContext2 queryCompilationContext) { - return new RelationalEntityQueryableTranslator(_model); + return new RelationalEntityQueryableTranslator(_model, _sqlExpressionFactory); } } } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitors.cs b/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitors.cs index 816a78bc2d1..f0802f47780 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitors.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalEntityQueryableExpressionVisitors.cs @@ -11,15 +11,17 @@ namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline public class RelationalEntityQueryableTranslator : EntityQueryableTranslator { private readonly IModel _model; + private readonly ISqlExpressionFactory _sqlExpressionFactory; - public RelationalEntityQueryableTranslator(IModel model) + public RelationalEntityQueryableTranslator(IModel model, ISqlExpressionFactory sqlExpressionFactory) { _model = model; + _sqlExpressionFactory = sqlExpressionFactory; } public override Expression Visit(Expression query) { - return new RelationalEntityQueryableExpressionVisitor2(_model).Visit(query); + return new RelationalEntityQueryableExpressionVisitor2(_model, _sqlExpressionFactory).Visit(query); } } } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs index bf7db33416c..dfc856c3e4d 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -11,6 +11,7 @@ using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.Pipeline; using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; @@ -57,7 +58,7 @@ protected override ShapedQueryExpression TranslateAll(ShapedQueryExpression sour } translation = _sqlExpressionFactory.Exists(selectExpression, true); - source.QueryExpression = selectExpression.SetProjectionAsResult(translation); + source.QueryExpression = _sqlExpressionFactory.Select(translation); source.ShaperExpression = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(bool)); return source; @@ -82,7 +83,7 @@ protected override ShapedQueryExpression TranslateAny(ShapedQueryExpression sour } var translation = _sqlExpressionFactory.Exists(selectExpression, false); - source.QueryExpression = selectExpression.SetProjectionAsResult(translation); + source.QueryExpression = _sqlExpressionFactory.Select(translation); source.ShaperExpression = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(bool)); return source; @@ -158,7 +159,7 @@ protected override ShapedQueryExpression TranslateContains(ShapedQueryExpression selectExpression.ApplyProjection(); translation = _sqlExpressionFactory.In(translation, selectExpression, false); - source.QueryExpression = selectExpression.SetProjectionAsResult(translation); + source.QueryExpression = _sqlExpressionFactory.Select(translation); source.ShaperExpression = new ProjectionBindingExpression(source.QueryExpression, new ProjectionMember(), typeof(bool)); return source; @@ -512,7 +513,53 @@ protected override ShapedQueryExpression TranslateMin(ShapedQueryExpression sour return AggregateResultShaper(source, projection, throwOnNullResult: true, resultType); } - protected override ShapedQueryExpression TranslateOfType(ShapedQueryExpression source, Type resultType) => throw new NotImplementedException(); + protected override ShapedQueryExpression TranslateOfType(ShapedQueryExpression source, Type resultType) + { + if (source.ShaperExpression is EntityShaperExpression entityShaperExpression) + { + var entityType = entityShaperExpression.EntityType; + if (entityType.ClrType == resultType) + { + return source; + } + + var baseType = entityType.GetAllBaseTypes().SingleOrDefault(et => et.ClrType == resultType); + if (baseType != null) + { + source.ShaperExpression = new EntityShaperExpression( + baseType, entityShaperExpression.ValueBufferExpression, entityShaperExpression.Nullable); + + return source; + } + + var derivedType = entityType.GetDerivedTypes().SingleOrDefault(et => et.ClrType == resultType); + if (derivedType != null) + { + var selectExpression = (SelectExpression)source.QueryExpression; + var concreteEntityTypes = derivedType.GetConcreteTypesInHierarchy().ToList(); + var discriminatorColumn = selectExpression + .BindProperty(entityShaperExpression.ValueBufferExpression, entityType.GetDiscriminatorProperty()); + + var predicate = concreteEntityTypes.Count == 1 + ? _sqlExpressionFactory.Equal(discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) + : (SqlExpression)_sqlExpressionFactory.In(discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue())), + negated: false); + + selectExpression.ApplyPredicate(predicate); + + source.ShaperExpression = new EntityShaperExpression( + derivedType, entityShaperExpression.ValueBufferExpression, entityShaperExpression.Nullable); + + return source; + } + + // If the resultType is not part of hierarchy then we don't know how to materialize. + } + + throw new NotImplementedException(); + } protected override ShapedQueryExpression TranslateOrderBy(ShapedQueryExpression source, LambdaExpression keySelector, bool ascending) { diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs index b1780a5b684..df9b80020a6 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryCompilingExpressionVisitor.cs @@ -388,8 +388,8 @@ private class RelationalProjectionBindingRemovingExpressionVisitor : ExpressionV public static readonly ParameterExpression DataReaderParameter = Expression.Parameter(typeof(DbDataReader), "dataReader"); - private readonly IDictionary _materializationContextBindings - = new Dictionary(); + private readonly IDictionary> _materializationContextBindings + = new Dictionary>(); public RelationalProjectionBindingRemovingExpressionVisitor(SelectExpression selectExpression) { @@ -405,7 +405,8 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) var newExpression = (NewExpression)binaryExpression.Right; var projectionBindingExpression = (ProjectionBindingExpression)newExpression.Arguments[0]; - _materializationContextBindings[parameterExpression] = GetProjectionIndex(projectionBindingExpression); + _materializationContextBindings[parameterExpression] + = (IDictionary)GetProjectionIndex(projectionBindingExpression); var updatedExpression = Expression.New(newExpression.Constructor, Expression.Constant(ValueBuffer.Empty), @@ -422,14 +423,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp if (methodCallExpression.Method.IsGenericMethod && methodCallExpression.Method.GetGenericMethodDefinition() == EntityMaterializerSource.TryReadValueMethod) { - var originalIndex = (int)((ConstantExpression)methodCallExpression.Arguments[1]).Value; - var indexOffset = methodCallExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression - ? GetProjectionIndex(projectionBindingExpression) - : _materializationContextBindings[(ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object]; - var property = (IProperty)((ConstantExpression)methodCallExpression.Arguments[2]).Value; + var propertyProjectionMap = methodCallExpression.Arguments[0] is ProjectionBindingExpression projectionBindingExpression + ? (IDictionary)GetProjectionIndex(projectionBindingExpression) + : _materializationContextBindings[(ParameterExpression)((MethodCallExpression)methodCallExpression.Arguments[0]).Object]; - var projectionIndex = originalIndex + indexOffset; + var projectionIndex = propertyProjectionMap[property]; var projection = _selectExpression.Projection[projectionIndex]; return CreateGetValueExpression( @@ -446,7 +445,7 @@ protected override Expression VisitExtension(Expression extensionExpression) { if (extensionExpression is ProjectionBindingExpression projectionBindingExpression) { - var projectionIndex = GetProjectionIndex(projectionBindingExpression); + var projectionIndex = (int)GetProjectionIndex(projectionBindingExpression); var projection = _selectExpression.Projection[projectionIndex]; return CreateGetValueExpression( @@ -459,11 +458,13 @@ protected override Expression VisitExtension(Expression extensionExpression) return base.VisitExtension(extensionExpression); } - private int GetProjectionIndex(ProjectionBindingExpression projectionBindingExpression) + private object GetProjectionIndex(ProjectionBindingExpression projectionBindingExpression) { return projectionBindingExpression.ProjectionMember != null - ? (int)((ConstantExpression)_selectExpression.GetProjectionExpression(projectionBindingExpression.ProjectionMember)).Value - : projectionBindingExpression.Index; + ? ((ConstantExpression)_selectExpression.GetProjectionExpression(projectionBindingExpression.ProjectionMember)).Value + : (projectionBindingExpression.Index != null + ? (object)projectionBindingExpression.Index + : projectionBindingExpression.IndexMap); } private static bool IsNullableProjection(ProjectionExpression projection) diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpression.cs b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpression.cs index dcbba284259..5318762091a 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpression.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalShapedQueryExpression.cs @@ -1,37 +1,17 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using Microsoft.EntityFrameworkCore.Metadata; +using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Query.Pipeline; -using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; -using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.Relational.Query.Pipeline { public class RelationalShapedQueryExpression : ShapedQueryExpression { - public RelationalShapedQueryExpression(IEntityType entityType) + public RelationalShapedQueryExpression(Expression queryExpression, Expression shaperExpression) { - QueryExpression = new SelectExpression(entityType); - ShaperExpression = new EntityShaperExpression( - entityType, - new ProjectionBindingExpression( - QueryExpression, - new ProjectionMember(), - typeof(ValueBuffer)), - false); - } - - public RelationalShapedQueryExpression(IEntityType entityType, string sql) - { - QueryExpression = new SelectExpression(entityType, sql); - ShaperExpression = new EntityShaperExpression( - entityType, - new ProjectionBindingExpression( - QueryExpression, - new ProjectionMember(), - typeof(ValueBuffer)), - false); + QueryExpression = queryExpression; + ShaperExpression = shaperExpression; } } } diff --git a/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs index f8c3e5822b2..744c11c727b 100644 --- a/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/Pipeline/RelationalSqlTranslatingExpressionVisitor.cs @@ -8,6 +8,7 @@ using System.Reflection; using Microsoft.EntityFrameworkCore.Extensions.Internal; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; using Microsoft.EntityFrameworkCore.Query.NavigationExpansion; using Microsoft.EntityFrameworkCore.Query.Pipeline; @@ -79,7 +80,7 @@ protected override Expression VisitMember(MemberExpression memberExpression) var innerExpression = Visit(memberExpression.Expression); if (innerExpression is EntityShaperExpression entityShaper) { - return BindProperty(entityShaper, memberExpression.Member.GetSimpleMemberName()); + return BindProperty(entityShaper, entityShaper.EntityType.FindProperty(memberExpression.Member.GetSimpleMemberName())); } return TranslationFailed(memberExpression.Expression, innerExpression) @@ -87,34 +88,78 @@ protected override Expression VisitMember(MemberExpression memberExpression) : _memberTranslatorProvider.Translate((SqlExpression)innerExpression, memberExpression.Member, memberExpression.Type); } - private Expression BindProperty(EntityShaperExpression entityShaper, string propertyName) + private SqlExpression BindProperty(EntityShaperExpression entityShaper, IProperty property) { - var property = entityShaper.EntityType.FindProperty(propertyName); - var selectExpression = (SelectExpression)entityShaper.ValueBufferExpression.QueryExpression; + return ((SelectExpression)entityShaper.ValueBufferExpression.QueryExpression) + .BindProperty(entityShaper.ValueBufferExpression, property); + } + + protected override Expression VisitTypeBinary(TypeBinaryExpression typeBinaryExpression) + { + if (typeBinaryExpression.NodeType == ExpressionType.TypeIs + && typeBinaryExpression.Expression is EntityShaperExpression entityShaperExpression) + { + var entityType = entityShaperExpression.EntityType; + if (entityType.GetAllBaseTypesInclusive().Any(et => et.ClrType == typeBinaryExpression.TypeOperand)) + { + return _sqlExpressionFactory.Constant(true); + } - return selectExpression.BindProperty(entityShaper.ValueBufferExpression, property); + var derivedType = entityType.GetDerivedTypes().SingleOrDefault(et => et.ClrType == typeBinaryExpression.TypeOperand); + if (derivedType != null) + { + var concreteEntityTypes = derivedType.GetConcreteTypesInHierarchy().ToList(); + var discriminatorColumn = BindProperty(entityShaperExpression, entityType.GetDiscriminatorProperty()); + + return concreteEntityTypes.Count == 1 + ? _sqlExpressionFactory.Equal(discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes[0].GetDiscriminatorValue())) + : (Expression)_sqlExpressionFactory.In(discriminatorColumn, + _sqlExpressionFactory.Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), + negated: false); + } + else + { + return _sqlExpressionFactory.Constant(false); + } + } + + return null; } protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) { if (methodCallExpression.TryGetEFPropertyArguments(out var source, out var propertyName)) { - // In certain cases EF.Property would have convert node around the source. + Type convertedType = null; if (source is UnaryExpression unaryExpression - && unaryExpression.NodeType == ExpressionType.Convert - && unaryExpression.Type == typeof(object)) + && unaryExpression.NodeType == ExpressionType.Convert) { source = unaryExpression.Operand; + if (unaryExpression.Type != typeof(object)) + { + convertedType = unaryExpression.Type; + } } if (source is EntityShaperExpression entityShaper) { - return BindProperty(entityShaper, propertyName); - } - else - { - throw new InvalidOperationException(); + 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(); } if (methodCallExpression.Method.DeclaringType == typeof(Queryable)) @@ -287,8 +332,6 @@ protected override Expression VisitUnary(UnaryExpression unaryExpression) return null; } - - var sqlOperand = (SqlExpression)operand; switch (unaryExpression.NodeType) diff --git a/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs b/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs index 5bd4f2a5378..c31955caf82 100644 --- a/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs +++ b/src/EFCore.Relational/Query/Pipeline/SqlExpressionFactory.cs @@ -5,6 +5,9 @@ using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Query.Pipeline; using Microsoft.EntityFrameworkCore.Relational.Query.Pipeline.SqlExpressions; using Microsoft.EntityFrameworkCore.Storage; @@ -500,6 +503,71 @@ public SqlConstantExpression Constant(object value, RelationalTypeMapping typeMa return new SqlConstantExpression(Expression.Constant(value), typeMapping); } + public SelectExpression Select(SqlExpression projection) + { + var selectExpression = new SelectExpression( + alias: null, + new List(), + new List(), + new List()); + + if (projection != null) + { + selectExpression.ReplaceProjection(new Dictionary + { + { new ProjectionMember(), projection } + }); + } + + return selectExpression; + } + + public SelectExpression Select(IEntityType entityType) + { + var selectExpression = new SelectExpression(entityType); + AddDiscriminator(selectExpression, entityType); + + return selectExpression; + } + + public SelectExpression Select(IEntityType entityType, string sql, Expression sqlArguments) + { + var selectExpression = new SelectExpression(entityType, sql, sqlArguments); + AddDiscriminator(selectExpression, entityType); + + return selectExpression; + } + + private void AddDiscriminator(SelectExpression selectExpression, IEntityType entityType) + { + var concreteEntityTypes = entityType.GetConcreteTypesInHierarchy().ToList(); + + if (concreteEntityTypes.Count == 1) + { + var concreteEntityType = concreteEntityTypes[0]; + // If not a derived type + if (concreteEntityType.RootType() == concreteEntityType) + { + return; + } + + var discriminatorColumn = ((EntityProjectionExpression)selectExpression.GetProjectionExpression(new ProjectionMember())) + .GetProperty(concreteEntityType.GetDiscriminatorProperty()); + + selectExpression.ApplyPredicate( + Equal(discriminatorColumn, Constant(concreteEntityType.GetDiscriminatorValue()))); + + } + else + { + var discriminatorColumn = ((EntityProjectionExpression)selectExpression.GetProjectionExpression(new ProjectionMember())) + .GetProperty(concreteEntityTypes[0].GetDiscriminatorProperty()); + + selectExpression.ApplyPredicate( + In(discriminatorColumn, Constant(concreteEntityTypes.Select(et => et.GetDiscriminatorValue()).ToList()), negated: false)); + } + } + #endregion } } diff --git a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/FromSqlExpression.cs b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/FromSqlExpression.cs index ce5e0a03860..7024e9b6a94 100644 --- a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/FromSqlExpression.cs +++ b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/FromSqlExpression.cs @@ -12,10 +12,12 @@ public class FromSqlExpression : TableExpressionBase #region Fields & Constructors public FromSqlExpression( [NotNull] string sql, + Expression arguments, [NotNull] string alias) : base(alias) { Sql = sql; + Arguments = arguments; } #endregion @@ -28,6 +30,7 @@ public FromSqlExpression( /// The SQL. /// public string Sql { get; } + public Expression Arguments { get; } #endregion diff --git a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs index 9b374a25cc8..514ec1a1a71 100644 --- a/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs +++ b/src/EFCore.Relational/Query/Pipeline/SqlExpressions/SelectExpression.cs @@ -2,11 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Query.Pipeline; using Microsoft.EntityFrameworkCore.Storage; @@ -31,7 +33,7 @@ private readonly IDictionary _projectio public SqlExpression Offset { get; private set; } public bool IsDistinct { get; private set; } - private SelectExpression( + internal SelectExpression( string alias, List projections, List tables, @@ -43,7 +45,7 @@ private SelectExpression( _orderings = orderings; } - public SelectExpression(IEntityType entityType) + internal SelectExpression(IEntityType entityType) : base("") { var tableExpression = new TableExpression( @@ -56,11 +58,12 @@ public SelectExpression(IEntityType entityType) _projectionMapping[new ProjectionMember()] = new EntityProjectionExpression(entityType, tableExpression, false); } - public SelectExpression(IEntityType entityType, string sql) + public SelectExpression(IEntityType entityType, string sql, Expression arguments) : base("") { var fromSqlExpression = new FromSqlExpression( sql, + arguments, entityType.GetTableName().ToLower().Substring(0, 1)); _tables.Add(fromSqlExpression); @@ -85,17 +88,21 @@ public void ApplyProjection() var result = new Dictionary(); foreach (var keyValuePair in _projectionMapping) { - result[keyValuePair.Key] = Constant(_projection.Count); if (keyValuePair.Value is EntityProjectionExpression entityProjection) { - foreach (var property in entityProjection.EntityType.GetProperties()) + var map = new Dictionary(); + foreach (var property in entityProjection.EntityType + .GetDerivedTypesInclusive().SelectMany(e => e.GetDeclaredProperties())) { var columnExpression = entityProjection.GetProperty(property); + map[property] = _projection.Count; _projection.Add(new ProjectionExpression(columnExpression, alias: "")); } + result[keyValuePair.Key] = Constant(map); } else { + result[keyValuePair.Key] = Constant(_projection.Count); _projection.Add(new ProjectionExpression((SqlExpression)keyValuePair.Value, alias: "")); } } @@ -103,21 +110,6 @@ public void ApplyProjection() _projectionMapping = result; } - public SelectExpression SetProjectionAsResult(SqlExpression sqlExpression) - { - return new SelectExpression( - null, - new List(), - new List(), - new List()) - { - _projectionMapping = new Dictionary - { - {new ProjectionMember(), sqlExpression } - } - }; - } - public void ReplaceProjection(IDictionary projectionMapping) { _projectionMapping.Clear(); @@ -150,14 +142,16 @@ public ProjectionBindingExpression AddToProjection(ProjectionBindingExpression p var entityProjection = (EntityProjectionExpression)_projectionMapping[projectionBindingExpression.ProjectionMember]; if (!_projectionCache.TryGetValue(entityProjection, out var result)) { - var index = _projection.Count; - foreach (var property in entityProjection.EntityType.GetProperties()) + var map = new Dictionary(); + foreach (var property in entityProjection.EntityType + .GetDerivedTypesInclusive().SelectMany(e => e.GetDeclaredProperties())) { var columnExpression = entityProjection.GetProperty(property); + map[property] = _projection.Count; _projection.Add(new ProjectionExpression(columnExpression, alias: "")); } - result = new ProjectionBindingExpression(this, index, typeof(ValueBuffer)); + result = new ProjectionBindingExpression(this, map); _projectionCache[entityProjection] = result; } @@ -286,7 +280,8 @@ public void PushdownIntoSubQuery() if (projection.Value is EntityProjectionExpression entityProjection) { var propertyExpressions = new Dictionary(); - foreach (var property in entityProjection.EntityType.GetProperties()) + foreach (var property in entityProjection.EntityType + .GetDerivedTypesInclusive().SelectMany(e => e.GetDeclaredProperties())) { var innerColumn = entityProjection.GetProperty(property); var projectionExpression = new ProjectionExpression(innerColumn, innerColumn.Name); diff --git a/src/EFCore/Query/Pipeline/ProjectionBindingExpression.cs b/src/EFCore/Query/Pipeline/ProjectionBindingExpression.cs index 8ce8a641982..8193d2c9255 100644 --- a/src/EFCore/Query/Pipeline/ProjectionBindingExpression.cs +++ b/src/EFCore/Query/Pipeline/ProjectionBindingExpression.cs @@ -2,9 +2,12 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections.Generic; using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Storage; namespace Microsoft.EntityFrameworkCore.Query.Pipeline { @@ -24,9 +27,17 @@ public ProjectionBindingExpression(Expression queryExpression, int index, Type t Type = type; } + public ProjectionBindingExpression(Expression queryExpression, IDictionary indexMap) + { + QueryExpression = queryExpression; + IndexMap = indexMap; + Type = typeof(ValueBuffer); + } + public Expression QueryExpression { get; } public ProjectionMember ProjectionMember { get; } - public int Index { get; } + public int? Index { get; } + public IDictionary IndexMap { get; } public override Type Type { get; } public override ExpressionType NodeType => ExpressionType.Extension; @@ -47,7 +58,9 @@ private bool Equals(ProjectionBindingExpression projectionBindingExpression) && (ProjectionMember == null ? projectionBindingExpression.ProjectionMember == null : ProjectionMember.Equals(projectionBindingExpression.ProjectionMember)) - && Index == projectionBindingExpression.Index; + && Index == projectionBindingExpression.Index + // Using reference equality here since if we are this far, we don't need to compare this. + && IndexMap == projectionBindingExpression.IndexMap; public override int GetHashCode() { @@ -56,7 +69,8 @@ public override int GetHashCode() var hashCode = base.GetHashCode(); hashCode = (hashCode * 397) ^ QueryExpression.GetHashCode(); hashCode = (hashCode * 397) ^ (ProjectionMember?.GetHashCode() ?? 0); - hashCode = (hashCode * 397) ^ Index.GetHashCode(); + hashCode = (hashCode * 397) ^ (Index?.GetHashCode() ?? 0); + hashCode = (hashCode * 397) ^ (IndexMap?.GetHashCode() ?? 0); return hashCode; } @@ -66,7 +80,25 @@ public override int GetHashCode() public void Print(ExpressionPrinter expressionPrinter) { - expressionPrinter.StringBuilder.Append(nameof(ProjectionBindingExpression) + ": " + ProjectionMember + "/" + Index); + expressionPrinter.StringBuilder.Append(nameof(ProjectionBindingExpression) + ": "); + if (ProjectionMember != null) + { + expressionPrinter.StringBuilder.Append(ProjectionMember); + } + else if (Index != null) + { + expressionPrinter.StringBuilder.Append(Index); + } + else + { + using (expressionPrinter.StringBuilder.Indent()) + { + foreach (var kvp in IndexMap) + { + expressionPrinter.StringBuilder.AppendLine($"{kvp.Key.Name}:{kvp.Value},"); + } + } + } } } } diff --git a/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs b/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs index 1df106524b5..398ae3bf39c 100644 --- a/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/ShapedQueryExpressionVisitor.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using System.Collections; using System.Collections.Generic; using System.Linq; using System.Linq.Expressions; @@ -291,7 +292,9 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres else { expressions.Add(Expression.Condition( - primaryKey.Properties + (primaryKey != null + ? primaryKey.Properties + : entityType.GetProperties()) .Select(p => Expression.Equal( _entityMaterializerSource.CreateReadValueExpression( @@ -301,8 +304,8 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres p), Expression.Constant(null))) .Aggregate((a, b) => Expression.OrElse(a, b)), - Expression.Constant(null, entityType.ClrType), - MaterializeEntity(entityType, valueBuffer))); + Expression.Constant(null, entityType.ClrType), + MaterializeEntity(entityType, valueBuffer))); } return Expression.Block(variables, expressions); @@ -311,9 +314,15 @@ private Expression ProcessEntityShaper(EntityShaperExpression entityShaperExpres private Expression MaterializeEntity(IEntityType entityType, Expression valueBuffer) { var expressions = new List(); + var variables = new List(); + var returnType = entityType.ClrType; + var valueBufferConstructor + = typeof(ValueBuffer).GetTypeInfo().DeclaredConstructors.Single(ci => ci.GetParameters().Length == 1); - var materializationContext = Expression.Variable(typeof(MaterializationContext), "materializationContext" + _currentEntityIndex); + var materializationContext = Expression.Variable( + typeof(MaterializationContext), "materializationContext" + _currentEntityIndex); + variables.Add(materializationContext); expressions.Add( Expression.Assign( materializationContext, @@ -324,75 +333,130 @@ private Expression MaterializeEntity(IEntityType entityType, Expression valueBuf QueryCompilationContext2.QueryContextParameter, _dbContextMemberInfo)))); - var materializationExpression - = _entityMaterializerSource.CreateMaterializeExpression( - entityType, - "instance" + _currentEntityIndex, - materializationContext); + var concreteEntityTypes = entityType.GetConcreteTypesInHierarchy().ToList(); - if (materializationExpression is BlockExpression blockExpression) + Expression materializationExpression = null; + Expression shadowValuesExpression = Expression.Constant(ValueBuffer.Empty); + if (concreteEntityTypes.Count == 1) { - expressions.AddRange(blockExpression.Expressions.Take(blockExpression.Expressions.Count - 1)); + materializationExpression + = _entityMaterializerSource.CreateMaterializeExpression( + concreteEntityTypes[0], + "instance" + _currentEntityIndex, + materializationContext); if (_trackQueryResults) { - expressions.Add( - Expression.Call( - QueryCompilationContext2.QueryContextParameter, - _startTrackingMethodInfo, - Expression.Constant(entityType), - blockExpression.Expressions.Last(), - Expression.New( - typeof(ValueBuffer).GetTypeInfo().DeclaredConstructors.Single(ci => ci.GetParameters().Length == 1), - Expression.NewArrayInit( + var shadowProperties = concreteEntityTypes[0].GetProperties().Where(p => p.IsShadowProperty()); + if (shadowProperties.Any()) + { + shadowValuesExpression = Expression.New( + valueBufferConstructor, + Expression.NewArrayInit( + typeof(object), + shadowProperties.Select(p => _entityMaterializerSource.CreateReadValueExpression( + valueBuffer, typeof(object), - entityType.GetProperties().Where(p => p.IsShadowProperty()) - .Select(p => _entityMaterializerSource.CreateReadValueExpression( - valueBuffer, - typeof(object), - p.GetIndex(), - p)))))); + p.GetIndex(), + p)))); + } } - - expressions.Add(blockExpression.Expressions.Last()); - - return Expression.Block( - entityType.ClrType, - new[] { materializationContext }.Concat(blockExpression.Variables), - expressions); } else { - var instanceVariable = Expression.Variable(materializationExpression.Type, "instance" + _currentEntityIndex); - expressions.Add(Expression.Assign(instanceVariable, materializationExpression)); + var firstEntityType = concreteEntityTypes[0]; + var discriminatorProperty = firstEntityType.GetDiscriminatorProperty(); + var discriminatorValueVariable = Expression.Variable(discriminatorProperty.ClrType); + variables.Add(discriminatorValueVariable); - if (_trackQueryResults) + expressions.Add( + Expression.Assign( + discriminatorValueVariable, + _entityMaterializerSource.CreateReadValueExpression( + valueBuffer, + discriminatorProperty.ClrType, + discriminatorProperty.GetIndex(), + discriminatorProperty))); + + materializationExpression = Expression.Block( + Expression.Throw( + Expression.Constant(new InvalidOperationException(/*RelationalStrings.UnableToDiscriminate(entityType.DisplayName())*/))), + Expression.Constant(null, returnType)); + + foreach (var concreteEntityType in concreteEntityTypes) { - expressions.Add( - Expression.Call( - QueryCompilationContext2.QueryContextParameter, - _startTrackingMethodInfo, - Expression.Constant(entityType), - instanceVariable, - Expression.New( - typeof(ValueBuffer).GetTypeInfo().DeclaredConstructors.Single(ci => ci.GetParameters().Length == 1), + var discriminatorValue + = Expression.Constant( + concreteEntityType.GetDiscriminatorValue(), + discriminatorProperty.ClrType); + + var materializer = _entityMaterializerSource + .CreateMaterializeExpression(concreteEntityType, "instance", materializationContext); + + materializationExpression = Expression.Condition( + Expression.Equal(discriminatorValueVariable, discriminatorValue), + Expression.Convert(materializer, returnType), + materializationExpression); + + if (_trackQueryResults) + { + var shadowProperties = concreteEntityType.GetProperties().Where(p => p.IsShadowProperty()); + var shadowValues = shadowProperties.Any() + ? Expression.New( + valueBufferConstructor, Expression.NewArrayInit( typeof(object), - entityType.GetProperties().Where(p => p.IsShadowProperty()) - .Select(p => _entityMaterializerSource.CreateReadValueExpression( - valueBuffer, - typeof(object), - p.GetIndex(), - p)))))); + shadowProperties.Select(p => _entityMaterializerSource.CreateReadValueExpression( + valueBuffer, + typeof(object), + p.GetIndex(), + p)))) + : (Expression)Expression.Constant(ValueBuffer.Empty); + + shadowValuesExpression = Expression.Condition( + Expression.Equal(discriminatorValueVariable, discriminatorValue), + shadowValues, + shadowValuesExpression); + + } } + } - expressions.Add(instanceVariable); + Expression result; + if (materializationExpression is BlockExpression blockExpression) + { + expressions.AddRange(blockExpression.Expressions.Take(blockExpression.Expressions.Count - 1)); + variables.AddRange(blockExpression.Variables); + result = blockExpression.Expressions.Last(); + } + else + { + // Possible expressions are + // NewExpression when only ctor initialization + // MethodCallExpression when using factory method to create entityType + var instanceVariable = Expression.Variable(returnType, "instance" + _currentEntityIndex); + variables.Add(instanceVariable); + expressions.Add(Expression.Assign(instanceVariable, materializationExpression)); + result = instanceVariable; + } - return Expression.Block( - entityType.ClrType, - new[] { materializationContext, instanceVariable }, - expressions); + if (_trackQueryResults) + { + expressions.Add( + Expression.Call( + QueryCompilationContext2.QueryContextParameter, + _startTrackingMethodInfo, + Expression.Constant(entityType), + result, + shadowValuesExpression)); } + + expressions.Add(result); + + return Expression.Block( + returnType, + variables, + expressions); } } } diff --git a/test/EFCore.InMemory.FunctionalTests/FindInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/FindInMemoryTest.cs index 2cbc44779f4..cb9e3c833db 100644 --- a/test/EFCore.InMemory.FunctionalTests/FindInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/FindInMemoryTest.cs @@ -13,7 +13,7 @@ protected FindInMemoryTest(FindInMemoryFixture fixture) { } - public class FindInMemoryTestSet : FindInMemoryTest + internal class FindInMemoryTestSet : FindInMemoryTest { public FindInMemoryTestSet(FindInMemoryFixture fixture) : base(fixture) @@ -27,7 +27,7 @@ protected override ValueTask FindAsync(DbContext context, para => context.Set().FindAsync(keyValues); } - public class FindInMemoryTestContext : FindInMemoryTest + internal class FindInMemoryTestContext : FindInMemoryTest { public FindInMemoryTestContext(FindInMemoryFixture fixture) : base(fixture) @@ -41,7 +41,7 @@ protected override ValueTask FindAsync(DbContext context, para => context.FindAsync(keyValues); } - public class FindInMemoryTestNonGeneric : FindInMemoryTest + internal class FindInMemoryTestNonGeneric : FindInMemoryTest { public FindInMemoryTestNonGeneric(FindInMemoryFixture fixture) : base(fixture) diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs index 4ab785fbde7..0eb8d41d1d6 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -40,6 +40,7 @@ public class InMemoryComplianceTest : ComplianceTestBase typeof(QueryTaggingTestBase<>), typeof(SpatialQueryTestBase<>), typeof(UpdatesTestBase<>), + typeof(FindTestBase<>), }; protected override Assembly TargetAssembly { get; } = typeof(InMemoryComplianceTest).Assembly; diff --git a/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs index 91a9c72b10e..919a0c0696a 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs @@ -18,7 +18,7 @@ protected InheritanceRelationalTestBase(TFixture fixture) { } - [Fact] + [Fact(Skip = "Issue#15704")] public virtual void FromSql_on_root() { using (var context = CreateContext()) @@ -27,7 +27,7 @@ public virtual void FromSql_on_root() } } - [Fact] + [Fact(Skip = "Issue#15704")] public virtual void FromSql_on_derived() { using (var context = CreateContext()) diff --git a/test/EFCore.Specification.Tests/DatabindingTestBase.cs b/test/EFCore.Specification.Tests/DatabindingTestBase.cs index 488ff88189a..5a42165ad9d 100644 --- a/test/EFCore.Specification.Tests/DatabindingTestBase.cs +++ b/test/EFCore.Specification.Tests/DatabindingTestBase.cs @@ -546,7 +546,7 @@ public virtual void Load_executes_query_on_DbQuery() } } - [Theory(Skip = "Tasklist#21")] + [Theory] [InlineData(false)] [InlineData(true)] public void LocalView_is_initialized_with_entities_from_the_context( diff --git a/test/EFCore.Specification.Tests/FindTestBase.cs b/test/EFCore.Specification.Tests/FindTestBase.cs index 9fcc25b2f62..365ef2e7c60 100644 --- a/test/EFCore.Specification.Tests/FindTestBase.cs +++ b/test/EFCore.Specification.Tests/FindTestBase.cs @@ -5,7 +5,6 @@ using System.ComponentModel.DataAnnotations.Schema; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; @@ -49,7 +48,7 @@ public virtual void Find_int_key_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Returns_null_for_int_key_not_in_store() { using (var context = CreateContext()) @@ -82,7 +81,7 @@ public virtual void Find_nullable_int_key_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Returns_null_for_nullable_int_key_not_in_store() { using (var context = CreateContext()) @@ -115,7 +114,7 @@ public virtual void Find_string_key_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Returns_null_for_string_key_not_in_store() { using (var context = CreateContext()) @@ -149,7 +148,7 @@ public virtual void Find_composite_key_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Returns_null_for_composite_key_not_in_store() { using (var context = CreateContext()) @@ -182,7 +181,7 @@ public virtual void Find_base_type_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Returns_null_for_base_type_not_in_store() { using (var context = CreateContext()) @@ -217,7 +216,7 @@ public virtual void Find_derived_type_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Returns_null_for_derived_type_not_in_store() { using (var context = CreateContext()) @@ -226,7 +225,7 @@ public virtual void Returns_null_for_derived_type_not_in_store() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Find_base_type_using_derived_set_tracked() { using (var context = CreateContext()) @@ -241,7 +240,7 @@ public virtual void Find_base_type_using_derived_set_tracked() } } - [Fact(Skip = "QueryIssue")] + [Fact] public virtual void Find_base_type_using_derived_set_from_store() { using (var context = CreateContext()) @@ -265,7 +264,7 @@ public virtual void Find_derived_type_using_base_set_tracked() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual void Find_derived_using_base_set_type_from_store() { using (var context = CreateContext()) @@ -298,7 +297,7 @@ public virtual void Find_shadow_key_from_store() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual void Returns_null_for_shadow_key_not_in_store() { using (var context = CreateContext()) @@ -415,7 +414,7 @@ public virtual async Task Find_int_key_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_int_key_from_store_async() { using (var context = CreateContext()) @@ -448,7 +447,7 @@ public virtual async Task Find_nullable_int_key_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_nullable_int_key_from_store_async() { using (var context = CreateContext()) @@ -481,7 +480,7 @@ public virtual async Task Find_string_key_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_string_key_from_store_async() { using (var context = CreateContext()) @@ -548,7 +547,7 @@ public virtual async Task Find_base_type_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_base_type_from_store_async() { using (var context = CreateContext()) @@ -581,7 +580,7 @@ public virtual async Task Find_derived_type_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_derived_type_from_store_async() { using (var context = CreateContext()) @@ -616,7 +615,7 @@ public virtual async Task Find_base_type_using_derived_set_tracked_async() } } - [Fact(Skip = "TaskList#21")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_base_type_using_derived_set_from_store_async() { using (var context = CreateContext()) @@ -640,7 +639,7 @@ public virtual async Task Find_derived_type_using_base_set_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_derived_using_base_set_type_from_store_async() { using (var context = CreateContext()) @@ -664,7 +663,7 @@ public virtual async Task Find_shadow_key_tracked_async() } } - [Fact(Skip = "QueryIssue")] + [Fact(Skip = "Issue#15535")] public virtual async Task Find_shadow_key_from_store_async() { using (var context = CreateContext()) diff --git a/test/EFCore.Specification.Tests/Query/InheritanceTestBase.cs b/test/EFCore.Specification.Tests/Query/InheritanceTestBase.cs index 52d7ab28379..203e673bd3e 100644 --- a/test/EFCore.Specification.Tests/Query/InheritanceTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/InheritanceTestBase.cs @@ -221,7 +221,7 @@ public virtual void Can_query_all_animal_views() { using (var context = CreateContext()) { - var animalQueries = context.Set().OrderBy(av => av.CountryId).ToList(); + var animalQueries = context.Set().AsNoTracking().OrderBy(av => av.CountryId).ToList(); Assert.Equal(2, animalQueries.Count); Assert.IsType(animalQueries[0]); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceRelationshipsQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceRelationshipsQuerySqlServerTest.cs index 6ed061ed462..26c54e04044 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceRelationshipsQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceRelationshipsQuerySqlServerTest.cs @@ -5,8 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // TODO: Issue#14630#21 - internal class InheritanceRelationshipsQuerySqlServerTest + public class InheritanceRelationshipsQuerySqlServerTest : InheritanceRelationshipsQueryTestBase { public InheritanceRelationshipsQuerySqlServerTest( diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs index c4c87f4aa20..cff7d7c81bd 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs @@ -10,8 +10,7 @@ // ReSharper disable InconsistentNaming namespace Microsoft.EntityFrameworkCore.Query { - // TODO: Issue #14630 - internal class InheritanceSqlServerTest : InheritanceRelationalTestBase + public class InheritanceSqlServerTest : InheritanceRelationalTestBase { public InheritanceSqlServerTest(InheritanceSqlServerFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs index 67d5e4ecc8d..3f0fcfb08b8 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs @@ -26,12 +26,10 @@ public class SqlServerComplianceTest : RelationalComplianceTestBase typeof(CompiledQueryTestBase<>), typeof(GearsOfWarQueryTestBase<>), typeof(InheritanceRelationshipsQueryTestBase<>), - typeof(InheritanceTestBase<>), typeof(QueryNavigationsTestBase<>), typeof(ConcurrencyDetectorRelationalTestBase<>), typeof(QueryTaggingTestBase<>), typeof(GearsOfWarFromSqlQueryTestBase<>), - typeof(InheritanceRelationalTestBase<>), typeof(QueryNoClientEvalTestBase<>), typeof(WarningsTestBase<>), typeof(AsyncFromSqlSprocQueryTestBase<>), diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceRelationshipsQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceRelationshipsQuerySqliteTest.cs index 39d21558e4e..ece37b84013 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceRelationshipsQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceRelationshipsQuerySqliteTest.cs @@ -3,8 +3,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // TODO: Issue#14630#21 - internal class InheritanceRelationshipsQuerySqliteTest : InheritanceRelationshipsQueryTestBase + public class InheritanceRelationshipsQuerySqliteTest : InheritanceRelationshipsQueryTestBase { public InheritanceRelationshipsQuerySqliteTest(InheritanceRelationshipsQuerySqliteFixture fixture) : base(fixture) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceSqliteTest.cs index c814fcd44b3..d6515ab2ca5 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/InheritanceSqliteTest.cs @@ -7,8 +7,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // TODO: Issue#14630#21 - internal class InheritanceSqliteTest : InheritanceRelationalTestBase + public class InheritanceSqliteTest : InheritanceRelationalTestBase { public InheritanceSqliteTest(InheritanceSqliteFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) diff --git a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs index b8a846b1cdd..11b09135e4c 100644 --- a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs @@ -30,12 +30,10 @@ public class SqliteComplianceTest : RelationalComplianceTestBase typeof(CompiledQueryTestBase<>), typeof(GearsOfWarQueryTestBase<>), typeof(InheritanceRelationshipsQueryTestBase<>), - typeof(InheritanceTestBase<>), typeof(QueryNavigationsTestBase<>), typeof(ConcurrencyDetectorRelationalTestBase<>), typeof(QueryTaggingTestBase<>), typeof(GearsOfWarFromSqlQueryTestBase<>), - typeof(InheritanceRelationalTestBase<>), typeof(QueryNoClientEvalTestBase<>), typeof(WarningsTestBase<>), };