From 906274326e2890c229ef57391886ed117b990929 Mon Sep 17 00:00:00 2001 From: Smit Patel Date: Thu, 6 Feb 2020 14:06:17 -0800 Subject: [PATCH] Query: Make IEntityType part of EntityQueryable Part of #9914 With shared entity types query root can no longer be identified using just type. Part of #18923 --- ...yableMethodTranslatingExpressionVisitor.cs | 24 ++++++++++++++++++ ...yableMethodTranslatingExpressionVisitor.cs | 10 +++++--- ...yableMethodTranslatingExpressionVisitor.cs | 14 ++++++----- src/EFCore/Internal/InternalDbSet.cs | 2 +- ...yFilterDefiningQueryRewritingConvention.cs | 25 +++++++++++++------ src/EFCore/Query/IEntityQueryable.cs | 23 +++++++++++++++++ .../Internal/AsyncQueryProviderExtensions.cs | 14 ++++++----- ...ntityEqualityRewritingExpressionVisitor.cs | 2 +- src/EFCore/Query/Internal/EntityQueryable`.cs | 22 +++++++++++----- .../Query/Internal/IDetachableContext.cs | 22 ---------------- ...ingExpressionVisitor.ExpressionVisitors.cs | 7 ++---- .../NavigationExpandingExpressionVisitor.cs | 6 ++--- .../ParameterExtractingExpressionVisitor.cs | 4 +-- ...yableMethodTranslatingExpressionVisitor.cs | 5 +++- 14 files changed, 117 insertions(+), 63 deletions(-) create mode 100644 src/EFCore/Query/IEntityQueryable.cs delete mode 100644 src/EFCore/Query/Internal/IDetachableContext.cs diff --git a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs index 4c4eb5bf47b..33b7d06e167 100644 --- a/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Cosmos/Query/Internal/CosmosQueryableMethodTranslatingExpressionVisitor.cs @@ -96,6 +96,7 @@ public override ShapedQueryExpression TranslateSubquery(Expression expression) /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// + [Obsolete("Use overload which takes IEntityType.")] protected override ShapedQueryExpression CreateShapedQueryExpression(Type elementType) { Check.NotNull(elementType, nameof(elementType)); @@ -114,6 +115,29 @@ protected override ShapedQueryExpression CreateShapedQueryExpression(Type elemen false)); } + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType) + { + Check.NotNull(entityType, nameof(entityType)); + + var selectExpression = _sqlExpressionFactory.Select(entityType); + + return new ShapedQueryExpression( + selectExpression, + new EntityShaperExpression( + entityType, + new ProjectionBindingExpression( + selectExpression, + new ProjectionMember(), + typeof(ValueBuffer)), + false)); + } + /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in diff --git a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs index 8f320b11b1b..13bb6e1f8cd 100644 --- a/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.InMemory/Query/Internal/InMemoryQueryableMethodTranslatingExpressionVisitor.cs @@ -50,6 +50,7 @@ protected InMemoryQueryableMethodTranslatingExpressionVisitor( protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVisitor() => new InMemoryQueryableMethodTranslatingExpressionVisitor(this); + [Obsolete("Use overload which takes IEntityType.")] protected override ShapedQueryExpression CreateShapedQueryExpression(Type elementType) { Check.NotNull(elementType, nameof(elementType)); @@ -57,7 +58,10 @@ protected override ShapedQueryExpression CreateShapedQueryExpression(Type elemen return CreateShapedQueryExpression(_model.FindEntityType(elementType)); } - private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType) + protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType) + => CreateShapedQueryExpressionStatic(entityType); + + private static ShapedQueryExpression CreateShapedQueryExpressionStatic(IEntityType entityType) { var queryExpression = new InMemoryQueryExpression(entityType); @@ -1033,7 +1037,7 @@ private Expression TryExpand(Expression source, MemberIdentity member) var foreignKey = navigation.ForeignKey; if (navigation.IsCollection) { - var innerShapedQuery = CreateShapedQueryExpression(targetEntityType); + var innerShapedQuery = CreateShapedQueryExpressionStatic(targetEntityType); var innerQueryExpression = (InMemoryQueryExpression)innerShapedQuery.QueryExpression; var makeNullable = foreignKey.PrincipalKey.Properties @@ -1081,7 +1085,7 @@ ProjectionBindingExpression projectionBindingExpression var innerShaper = entityProjectionExpression.BindNavigation(navigation); if (innerShaper == null) { - var innerShapedQuery = CreateShapedQueryExpression(targetEntityType); + var innerShapedQuery = CreateShapedQueryExpressionStatic(targetEntityType); var innerQueryExpression = (InMemoryQueryExpression)innerShapedQuery.QueryExpression; var makeNullable = foreignKey.PrincipalKey.Properties diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 04e2820db79..d15f9f04911 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -71,8 +71,10 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp && methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSqlOnQueryable)) { var sql = (string)((ConstantExpression)methodCallExpression.Arguments[1]).Value; - var queryable = (IQueryable)((ConstantExpression)methodCallExpression.Arguments[0]).Value; - return CreateShapedQueryExpression(queryable.ElementType, sql, methodCallExpression.Arguments[2]); + var queryable = (IEntityQueryable)((ConstantExpression)methodCallExpression.Arguments[0]).Value; + + return CreateShapedQueryExpression( + queryable.EntityType, _sqlExpressionFactory.Select(queryable.EntityType, sql, methodCallExpression.Arguments[2])); } return base.VisitMethodCall(methodCallExpression); @@ -81,6 +83,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp protected override QueryableMethodTranslatingExpressionVisitor CreateSubqueryVisitor() => new RelationalQueryableMethodTranslatingExpressionVisitor(this); + [Obsolete("Use overload which takes IEntityType.")] protected override ShapedQueryExpression CreateShapedQueryExpression(Type elementType) { Check.NotNull(elementType, nameof(elementType)); @@ -91,12 +94,11 @@ protected override ShapedQueryExpression CreateShapedQueryExpression(Type elemen return CreateShapedQueryExpression(entityType, queryExpression); } - private ShapedQueryExpression CreateShapedQueryExpression(Type elementType, string sql, Expression arguments) + protected override ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType) { - var entityType = _model.FindEntityType(elementType); - var queryExpression = _sqlExpressionFactory.Select(entityType, sql, arguments); + Check.NotNull(entityType, nameof(entityType)); - return CreateShapedQueryExpression(entityType, queryExpression); + return CreateShapedQueryExpression(entityType, _sqlExpressionFactory.Select(entityType)); } private static ShapedQueryExpression CreateShapedQueryExpression(IEntityType entityType, SelectExpression selectExpression) diff --git a/src/EFCore/Internal/InternalDbSet.cs b/src/EFCore/Internal/InternalDbSet.cs index 774809d60b7..a8047c70350 100644 --- a/src/EFCore/Internal/InternalDbSet.cs +++ b/src/EFCore/Internal/InternalDbSet.cs @@ -116,7 +116,7 @@ private EntityQueryable EntityQueryable } private EntityQueryable CreateEntityQueryable() - => new EntityQueryable(_context.GetDependencies().QueryProvider); + => new EntityQueryable(_context.GetDependencies().QueryProvider, EntityType); /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs b/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs index 60a105644cc..9ddba82d7a6 100644 --- a/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs +++ b/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs @@ -52,13 +52,13 @@ public virtual void ProcessModelFinalized( var queryFilter = entityType.GetQueryFilter(); if (queryFilter != null) { - entityType.SetQueryFilter((LambdaExpression)DbSetAccessRewriter.Visit(queryFilter)); + entityType.SetQueryFilter((LambdaExpression)DbSetAccessRewriter.Rewrite(modelBuilder.Metadata, queryFilter)); } var definingQuery = entityType.GetDefiningQuery(); if (definingQuery != null) { - entityType.SetDefiningQuery((LambdaExpression)DbSetAccessRewriter.Visit(definingQuery)); + entityType.SetDefiningQuery((LambdaExpression)DbSetAccessRewriter.Rewrite(modelBuilder.Metadata, definingQuery)); } } } @@ -66,12 +66,20 @@ public virtual void ProcessModelFinalized( protected class DbSetAccessRewritingExpressionVisitor : ExpressionVisitor { private readonly Type _contextType; + private IModel _model; public DbSetAccessRewritingExpressionVisitor(Type contextType) { _contextType = contextType; } + public Expression Rewrite(IModel model, Expression expression) + { + _model = model; + + return Visit(expression); + } + protected override Expression VisitMember(MemberExpression memberExpression) { Check.NotNull(memberExpression, nameof(memberExpression)); @@ -80,9 +88,10 @@ protected override Expression VisitMember(MemberExpression memberExpression) && (memberExpression.Expression.Type.IsAssignableFrom(_contextType) || _contextType.IsAssignableFrom(memberExpression.Expression.Type)) && memberExpression.Type.IsGenericType - && memberExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>)) + && memberExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>) + && _model != null) { - return NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(memberExpression.Type.GetGenericArguments()[0]); + return NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(FindEntityType(memberExpression.Type)); } return base.VisitMember(memberExpression); @@ -96,14 +105,16 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp && methodCallExpression.Object != null && typeof(DbContext).IsAssignableFrom(methodCallExpression.Object.Type) && methodCallExpression.Type.IsGenericType - && methodCallExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>)) + && methodCallExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>) + && _model != null) { - return NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression( - methodCallExpression.Type.GetGenericArguments()[0]); + return NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(FindEntityType(methodCallExpression.Type)); } return base.VisitMethodCall(methodCallExpression); } + + private IEntityType FindEntityType(Type dbSetType) => _model.FindRuntimeEntityType(dbSetType.GetGenericArguments()[0]); } } } diff --git a/src/EFCore/Query/IEntityQueryable.cs b/src/EFCore/Query/IEntityQueryable.cs new file mode 100644 index 00000000000..3ed3f4a0e2d --- /dev/null +++ b/src/EFCore/Query/IEntityQueryable.cs @@ -0,0 +1,23 @@ +// 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; + +namespace Microsoft.EntityFrameworkCore.Query +{ + /// + /// An interface to identify query roots in LINQ. + /// + public interface IEntityQueryable + { + /// + /// Detach context if associated with this query root. + /// + IEntityQueryable DetachContext(); + + /// + /// Return entity type this query root references. + /// + IEntityType EntityType { get; } + } +} diff --git a/src/EFCore/Query/Internal/AsyncQueryProviderExtensions.cs b/src/EFCore/Query/Internal/AsyncQueryProviderExtensions.cs index 0c36dbdb149..0575f67fab4 100644 --- a/src/EFCore/Query/Internal/AsyncQueryProviderExtensions.cs +++ b/src/EFCore/Query/Internal/AsyncQueryProviderExtensions.cs @@ -5,6 +5,7 @@ using System.Linq.Expressions; using System.Reflection; using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query.Internal @@ -24,16 +25,16 @@ public static class AsyncQueryProviderExtensions /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public static ConstantExpression CreateEntityQueryableExpression( - [NotNull] this IAsyncQueryProvider entityQueryProvider, [NotNull] Type type) + [NotNull] this IAsyncQueryProvider entityQueryProvider, [NotNull] IEntityType entityType) { Check.NotNull(entityQueryProvider, nameof(entityQueryProvider)); - Check.NotNull(type, nameof(type)); + Check.NotNull(entityType, nameof(entityType)); return Expression.Constant( _createEntityQueryableMethod - .MakeGenericMethod(type) + .MakeGenericMethod(entityType.ClrType) .Invoke( - null, new object[] { entityQueryProvider })); + null, new object[] { entityQueryProvider, entityType })); } private static readonly MethodInfo _createEntityQueryableMethod @@ -41,7 +42,8 @@ private static readonly MethodInfo _createEntityQueryableMethod .GetTypeInfo().GetDeclaredMethod(nameof(CreateEntityQueryable)); [UsedImplicitly] - private static EntityQueryable CreateEntityQueryable(IAsyncQueryProvider entityQueryProvider) - => new EntityQueryable(entityQueryProvider); + private static EntityQueryable CreateEntityQueryable( + IAsyncQueryProvider entityQueryProvider, IEntityType entityType) + => new EntityQueryable(entityQueryProvider, entityType); } } diff --git a/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs b/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs index 109cb9b0b9a..6fc4dc3d4bf 100644 --- a/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/EntityEqualityRewritingExpressionVisitor.cs @@ -58,7 +58,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio return constantExpression.IsEntityQueryable() ? new EntityReferenceExpression( constantExpression, - _queryCompilationContext.Model.FindEntityType(((IQueryable)constantExpression.Value).ElementType)) + ((IEntityQueryable)constantExpression.Value).EntityType) : (Expression)constantExpression; } diff --git a/src/EFCore/Query/Internal/EntityQueryable`.cs b/src/EFCore/Query/Internal/EntityQueryable`.cs index 7a20dcaf9a6..dab658ae9a5 100644 --- a/src/EFCore/Query/Internal/EntityQueryable`.cs +++ b/src/EFCore/Query/Internal/EntityQueryable`.cs @@ -11,6 +11,7 @@ using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query.Internal @@ -24,13 +25,11 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal public class EntityQueryable : IOrderedQueryable, IAsyncEnumerable, - IDetachableContext, + IEntityQueryable, IListSource { - private static readonly EntityQueryable _detached - = new EntityQueryable(NullAsyncQueryProvider.Instance); - private readonly IAsyncQueryProvider _queryProvider; + private readonly IEntityType _entityType; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to @@ -38,11 +37,13 @@ private static readonly EntityQueryable _detached /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - public EntityQueryable([NotNull] IAsyncQueryProvider queryProvider) + public EntityQueryable([NotNull] IAsyncQueryProvider queryProvider, [NotNull] IEntityType entityType) { Check.NotNull(queryProvider, nameof(queryProvider)); + Check.NotNull(entityType, nameof(entityType)); _queryProvider = queryProvider; + _entityType = entityType; Expression = Expression.Constant(this); } @@ -118,7 +119,16 @@ public virtual IAsyncEnumerator GetAsyncEnumerator(CancellationToken ca /// any release. You should only use it directly in your code with extreme caution and knowing that /// doing so can result in application failures when updating to a new Entity Framework Core release. /// - IDetachableContext IDetachableContext.DetachContext() => _detached; + IEntityQueryable IEntityQueryable.DetachContext() + => new EntityQueryable(NullAsyncQueryProvider.Instance, _entityType); + + /// + /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to + /// the same compatibility standards as public APIs. It may be changed or removed without notice in + /// any release. You should only use it directly in your code with extreme caution and knowing that + /// doing so can result in application failures when updating to a new Entity Framework Core release. + /// + IEntityType IEntityQueryable.EntityType => _entityType; /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to diff --git a/src/EFCore/Query/Internal/IDetachableContext.cs b/src/EFCore/Query/Internal/IDetachableContext.cs deleted file mode 100644 index 9bdecc8eb23..00000000000 --- a/src/EFCore/Query/Internal/IDetachableContext.cs +++ /dev/null @@ -1,22 +0,0 @@ -// 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. - -namespace Microsoft.EntityFrameworkCore.Query.Internal -{ - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public interface IDetachableContext - { - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - IDetachableContext DetachContext(); - } -} diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index a92c7062503..b39934c83e6 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -176,8 +176,7 @@ protected Expression ExpandNavigation( return ownedExpansion; } - var innerQueryableType = targetType.ClrType; - var innerQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(innerQueryableType); + var innerQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(targetType); var innerSource = (NavigationExpansionExpression)_navigationExpandingExpressionVisitor.Visit(innerQueryable); if (entityReference.IncludePaths.ContainsKey(navigation)) { @@ -685,9 +684,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio if (constantExpression.IsEntityQueryable()) { - var entityType = - _navigationExpandingExpressionVisitor._queryCompilationContext.Model.FindEntityType( - ((IQueryable)constantExpression.Value).ElementType); + var entityType = ((IEntityQueryable)constantExpression.Value).EntityType; if (entityType == _entityType) { return _navigationExpandingExpressionVisitor.CreateNavigationExpansionExpression(constantExpression, entityType); diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index f7a34f72d00..0d2a0a43dc2 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -108,7 +108,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio if (constantExpression.IsEntityQueryable()) { - var entityType = _queryCompilationContext.Model.FindEntityType(((IQueryable)constantExpression.Value).ElementType); + var entityType = ((IEntityQueryable)constantExpression.Value).EntityType; var definingQuery = entityType.GetDefiningQuery(); NavigationExpansionExpression navigationExpansionExpression; if (definingQuery != null) @@ -524,7 +524,7 @@ when QueryableMethods.IsSumWithSelector(method): && (methodCallExpression.Arguments[2] is ParameterExpression || methodCallExpression.Arguments[2] is ConstantExpression) && constantExpression.IsEntityQueryable()) { - var entityType = _queryCompilationContext.Model.FindEntityType(((IQueryable)constantExpression.Value).ElementType); + var entityType = ((IEntityQueryable)constantExpression.Value).EntityType; var source = CreateNavigationExpansionExpression(constantExpression, entityType); source.UpdateSource( methodCallExpression.Update( @@ -1169,7 +1169,7 @@ private Expression ApplyQueryFilter(NavigationExpansionExpression navigationExpa // entity information through. Construct a MethodCall wrapper for the predicate with the proper query root. var filterWrapper = Expression.Call( QueryableMethods.Where.MakeGenericMethod(rootEntityType.ClrType), - NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(rootEntityType.ClrType), + NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(rootEntityType), filterPredicate); var rewrittenFilterWrapper = (MethodCallExpression)_entityEqualityRewritingExpressionVisitor.Rewrite(filterWrapper); filterPredicate = rewrittenFilterWrapper.Arguments[1].UnwrapLambdaFromQuote(); diff --git a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs index 2b2ecf1c48c..0acb667f234 100644 --- a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs @@ -253,7 +253,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { Check.NotNull(constantExpression, nameof(constantExpression)); - if (constantExpression.Value is IDetachableContext detachableContext) + if (constantExpression.Value is IEntityQueryable detachableContext) { var queryProvider = ((IQueryable)constantExpression.Value).Provider; if (_currentQueryProvider == null) @@ -629,7 +629,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { Check.NotNull(constantExpression, nameof(constantExpression)); - _evaluatable = !(constantExpression.Value is IDetachableContext) + _evaluatable = !(constantExpression.Value is IEntityQueryable) && !(constantExpression.Value is IQueryable); #pragma warning disable RCS1096 // Use bitwise operation instead of calling 'HasFlag'. diff --git a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs index 120c2c5c71b..9780979d278 100644 --- a/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore/Query/QueryableMethodTranslatingExpressionVisitor.cs @@ -10,6 +10,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query @@ -37,7 +38,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio Check.NotNull(constantExpression, nameof(constantExpression)); return constantExpression.IsEntityQueryable() - ? CreateShapedQueryExpression(((IQueryable)constantExpression.Value).ElementType) + ? CreateShapedQueryExpression(((IEntityQueryable)constantExpression.Value).EntityType) : base.VisitConstant(constantExpression); } @@ -559,7 +560,9 @@ public virtual ShapedQueryExpression TranslateSubquery([NotNull] Expression expr protected abstract QueryableMethodTranslatingExpressionVisitor CreateSubqueryVisitor(); + [Obsolete("Use overload which takes IEntityType.")] protected abstract ShapedQueryExpression CreateShapedQueryExpression([NotNull] Type elementType); + protected abstract ShapedQueryExpression CreateShapedQueryExpression([NotNull] IEntityType entityType); protected abstract ShapedQueryExpression TranslateAll([NotNull] ShapedQueryExpression source, [NotNull] LambdaExpression predicate); protected abstract ShapedQueryExpression TranslateAny([NotNull] ShapedQueryExpression source, [NotNull] LambdaExpression predicate);