From 3085673245fc5e42e95c571b488978508ef90879 Mon Sep 17 00:00:00 2001 From: Maurycy Markowski Date: Mon, 12 Aug 2019 13:04:01 -0700 Subject: [PATCH] Fix to #16323 - Query: add support for keyless entity types with defining query During nav expansion, when visiting EntityQueryables we now peek into entity metadata looking for defining query and query filter. If they are found, nav expansion applies them and expands navigations in the resulting query (recursively). Also, fix to #17111 - Cannot use DbSet in query filter DbSet accesses are converted into EntityQueryables during model finialization. We need to do this early, because otherwise we would need a dbcontext to create a queryable, and that dbcontext could be disposed at the time nav expansion runs. Since FromSql/FromSqlRaw/FromSqlInterpolated are constrained to DbSet they need to be converted to FromSqlOnQueryable at the same time. --- .../RelationalQueryableExtensions.cs | 10 +- .../RelationalConventionSetBuilder.cs | 4 + ...yFilterDefiningQueryRewritingConvention.cs | 82 +++++++++++ ...yableMethodTranslatingExpressionVisitor.cs | 1 - ...omSqlParameterApplyingExpressionVisitor.cs | 7 +- .../ProviderConventionSetBuilder.cs | 1 + ...yFilterDefiningQueryRewritingConvention.cs | 105 ++++++++++++++ src/EFCore/Properties/CoreStrings.Designer.cs | 9 ++ src/EFCore/Properties/CoreStrings.resx | 3 + .../NavigationExpandingExpressionVisitor.cs | 60 ++++++-- .../ParameterExtractingExpressionVisitor.cs | 3 +- ...ueryTranslationPreprocessorDependencies.cs | 1 - .../Query/ReplacingExpressionVisitor.cs | 7 +- .../SimpleQueryCosmosTest.KeylessEntities.cs | 55 ++------ .../Query/InheritanceRelationalTestBase.cs | 4 - .../Northwind/NorthwindRelationalContext.cs | 5 +- .../QueryFilterFuncletizationTestBase.cs | 4 +- .../SimpleQueryTestBase.KeylessEntities.cs | 72 ++++++---- .../Query/CompiledQuerySqlServerTest.cs | 53 +++++-- .../Query/InheritanceSqlServerTest.cs | 4 +- .../Query/QueryBugsTest.cs | 2 +- .../QueryFilterFuncletizationSqlServerTest.cs | 19 ++- ...impleQuerySqlServerTest.KeylessEntities.cs | 133 ++++++------------ .../Query/CompiledQueryInMemoryTest.cs | 18 --- .../Query/SimpleQuerySqliteTest.cs | 12 -- .../WithConstructorsSqliteTest.cs | 5 - 26 files changed, 432 insertions(+), 247 deletions(-) create mode 100644 src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterDefiningQueryRewritingConvention.cs create mode 100644 src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs diff --git a/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs b/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs index 0f9e25cd343..2adbf428d6b 100644 --- a/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs +++ b/src/EFCore.Relational/Extensions/RelationalQueryableExtensions.cs @@ -17,7 +17,7 @@ namespace Microsoft.EntityFrameworkCore /// public static class RelationalQueryableExtensions { - private static readonly MethodInfo _fromSqlOnQueryableMethodInfo + internal static readonly MethodInfo FromSqlOnQueryableMethodInfo = typeof(RelationalQueryableExtensions) .GetTypeInfo().GetDeclaredMethods(nameof(FromSqlOnQueryable)) .Single(); @@ -71,7 +71,7 @@ public static IQueryable FromSql( return source.Provider.CreateQuery( Expression.Call( null, - _fromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), + FromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), source.Expression, Expression.Constant(sql.Format), Expression.Constant(parameters))); @@ -115,7 +115,7 @@ public static IQueryable FromSql( return source.Provider.CreateQuery( Expression.Call( null, - _fromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), + FromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), source.Expression, Expression.Constant(sql.Format), Expression.Constant(sql.GetArguments()))); @@ -164,7 +164,7 @@ public static IQueryable FromSqlRaw( return queryableSource.Provider.CreateQuery( Expression.Call( null, - _fromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), + FromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), queryableSource.Expression, Expression.Constant(sql), Expression.Constant(parameters))); @@ -204,7 +204,7 @@ public static IQueryable FromSqlInterpolated( return queryableSource.Provider.CreateQuery( Expression.Call( null, - _fromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), + FromSqlOnQueryableMethodInfo.MakeGenericMethod(typeof(TEntity)), queryableSource.Expression, Expression.Constant(sql.Format), Expression.Constant(sql.GetArguments()))); diff --git a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs index bba0d820b57..aef97f135e0 100644 --- a/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs +++ b/src/EFCore.Relational/Metadata/Conventions/Infrastructure/RelationalConventionSetBuilder.cs @@ -101,6 +101,10 @@ public override ConventionSet CreateConventionSet() new DbFunctionTypeMappingConvention(Dependencies, RelationalDependencies), typeof(ValidatingConvention)); + ReplaceConvention( + conventionSet.ModelFinalizedConventions, + (QueryFilterDefiningQueryRewritingConvention)new RelationalQueryFilterDefiningQueryRewritingConvention(Dependencies, RelationalDependencies)); + return conventionSet; } } diff --git a/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterDefiningQueryRewritingConvention.cs b/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterDefiningQueryRewritingConvention.cs new file mode 100644 index 00000000000..8f65a63d06b --- /dev/null +++ b/src/EFCore.Relational/Metadata/Conventions/RelationalQueryFilterDefiningQueryRewritingConvention.cs @@ -0,0 +1,82 @@ +// 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 System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; + +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions +{ + public class RelationalQueryFilterDefiningQueryRewritingConvention : QueryFilterDefiningQueryRewritingConvention + { + /// + /// Creates a new instance of . + /// + /// Parameter object containing dependencies for this convention. + /// Parameter object containing relational dependencies for this convention. + public RelationalQueryFilterDefiningQueryRewritingConvention( + [NotNull] ProviderConventionSetBuilderDependencies dependencies, + [NotNull] RelationalConventionSetBuilderDependencies relationalDependencies) + : base(dependencies) + { + DbSetAccessRewriter = new RelationalDbSetAccessRewritingExpressionVisitor(Dependencies.ContextType); + } + + protected class RelationalDbSetAccessRewritingExpressionVisitor : DbSetAccessRewritingExpressionVisitor + { + public RelationalDbSetAccessRewritingExpressionVisitor(Type contextType) + : base(contextType) + { + } + + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) + { + if (methodCallExpression.Method.DeclaringType == typeof(RelationalQueryableExtensions) + && (methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSql) + || methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSqlRaw) + || methodCallExpression.Method.Name == nameof(RelationalQueryableExtensions.FromSqlInterpolated))) + { + var newSource = Visit(methodCallExpression.Arguments[0]); + var fromSqlOnQueryableMethod = RelationalQueryableExtensions.FromSqlOnQueryableMethodInfo.MakeGenericMethod(newSource.Type.GetGenericArguments()[0]); + + switch (methodCallExpression.Method.Name) + { + case nameof(RelationalQueryableExtensions.FromSqlRaw): + return Expression.Call( + null, + fromSqlOnQueryableMethod, + newSource, + methodCallExpression.Arguments[1], + methodCallExpression.Arguments[2]); + + case nameof(RelationalQueryableExtensions.FromSqlInterpolated): + case nameof(RelationalQueryableExtensions.FromSql) when methodCallExpression.Arguments.Count == 2: + var formattableString = Expression.Lambda>(Expression.Convert(methodCallExpression.Arguments[1], typeof(FormattableString))).Compile().Invoke(); + + return Expression.Call( + null, + fromSqlOnQueryableMethod, + newSource, + Expression.Constant(formattableString.Format), + Expression.Constant(formattableString.GetArguments())); + + case nameof(RelationalQueryableExtensions.FromSql) when methodCallExpression.Arguments.Count == 3: +#pragma warning disable CS0618 // Type or member is obsolete + var rawSqlStringString = Expression.Lambda>(Expression.Convert(methodCallExpression.Arguments[1], typeof(RawSqlString))).Compile().Invoke(); +#pragma warning restore CS0618 // Type or member is obsolete + + return Expression.Call( + null, + fromSqlOnQueryableMethod, + newSource, + Expression.Constant(rawSqlStringString.Format), + methodCallExpression.Arguments[2]); + } + } + + return base.VisitMethodCall(methodCallExpression); + } + } + } +} diff --git a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs index 05cbc8a0b0e..8a77a8baf30 100644 --- a/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalQueryableMethodTranslatingExpressionVisitor.cs @@ -7,7 +7,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Runtime.InteropServices.ComTypes; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; diff --git a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.FromSqlParameterApplyingExpressionVisitor.cs b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.FromSqlParameterApplyingExpressionVisitor.cs index 3a3942e5b16..c8e858d27cc 100644 --- a/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.FromSqlParameterApplyingExpressionVisitor.cs +++ b/src/EFCore.Relational/Query/RelationalShapedQueryCompilingExpressionVisitor.FromSqlParameterApplyingExpressionVisitor.cs @@ -84,10 +84,11 @@ public override Expression Visit(Expression expression) break; case ConstantExpression constantExpression: - var constantValues = (object[])constantExpression.Value; - for (var i = 0; i < constantValues.Length; i++) + var existingValues = (object[])constantExpression.Value; + var constantValues = new object[existingValues.Length]; + for (var i = 0; i < existingValues.Length; i++) { - var value = constantValues[i]; + var value = existingValues[i]; if (value is DbParameter dbParameter) { var parameterName = _parameterNameGenerator.GenerateNext(); diff --git a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs index 5e6b8d7e7c3..92360b5b83d 100644 --- a/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs +++ b/src/EFCore/Metadata/Conventions/Infrastructure/ProviderConventionSetBuilder.cs @@ -175,6 +175,7 @@ public virtual ConventionSet CreateConventionSet() conventionSet.ModelFinalizedConventions.Add(servicePropertyDiscoveryConvention); conventionSet.ModelFinalizedConventions.Add(nonNullableReferencePropertyConvention); conventionSet.ModelFinalizedConventions.Add(nonNullableNavigationConvention); + conventionSet.ModelFinalizedConventions.Add(new QueryFilterDefiningQueryRewritingConvention(Dependencies)); conventionSet.ModelFinalizedConventions.Add(new ValidatingConvention(Dependencies)); // Don't add any more conventions to ModelFinalizedConventions after ValidatingConvention diff --git a/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs b/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs new file mode 100644 index 00000000000..fa56f2391af --- /dev/null +++ b/src/EFCore/Metadata/Conventions/QueryFilterDefiningQueryRewritingConvention.cs @@ -0,0 +1,105 @@ +// 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 System; +using System.Diagnostics.CodeAnalysis; +using System.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Metadata.Builders; +using Microsoft.EntityFrameworkCore.Metadata.Conventions.Infrastructure; +using Microsoft.EntityFrameworkCore.Query.Internal; + +namespace Microsoft.EntityFrameworkCore.Metadata.Conventions +{ + /// + /// Convention that converts accesses of DbSets inside query filters and defining queries into EntityQueryables. + /// This makes them consistent with how DbSet accesses in the actual queries are represented, which allows for easier processing in the query pipeline. + /// + public class QueryFilterDefiningQueryRewritingConvention : IModelFinalizedConvention + { + /// + /// Creates a new instance of . + /// + /// Parameter object containing dependencies for this convention. + public QueryFilterDefiningQueryRewritingConvention([NotNull] ProviderConventionSetBuilderDependencies dependencies) + { + Dependencies = dependencies; + DbSetAccessRewriter = new DbSetAccessRewritingExpressionVisitor(dependencies.ContextType); + } + + /// + /// Parameter object containing service dependencies. + /// + protected virtual ProviderConventionSetBuilderDependencies Dependencies { get; } + + /// + /// Visitor used to rewrite DbSets accesses encountered in query filters and defining queries to EntityQueryables. + /// + protected virtual DbSetAccessRewritingExpressionVisitor DbSetAccessRewriter { get; [param: NotNull] set; } + + /// + /// Called after a model is finalized. + /// + /// The builder for the model. + /// Additional information associated with convention execution. + public virtual void ProcessModelFinalized( + IConventionModelBuilder modelBuilder, + IConventionContext context) + { + foreach (var entityType in modelBuilder.Metadata.GetEntityTypes()) + { + var queryFilter = entityType.GetQueryFilter(); + if (queryFilter != null) + { + entityType.SetQueryFilter((LambdaExpression)DbSetAccessRewriter.Visit(queryFilter)); + } + + var definingQuery = entityType.GetDefiningQuery(); + if (definingQuery != null) + { + entityType.SetDefiningQuery((LambdaExpression)DbSetAccessRewriter.Visit(definingQuery)); + } + } + } + + protected class DbSetAccessRewritingExpressionVisitor : ExpressionVisitor + { + private readonly Type _contextType; + + public DbSetAccessRewritingExpressionVisitor(Type contextType) + { + _contextType = contextType; + } + + protected override Expression VisitMember(MemberExpression memberExpression) + { + if (memberExpression.Expression != null + && (memberExpression.Expression.Type.IsAssignableFrom(_contextType) + || _contextType.IsAssignableFrom(memberExpression.Expression.Type)) + && memberExpression.Type.IsGenericType + && (memberExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>) +#pragma warning disable CS0618 // Type or member is obsolete + || memberExpression.Type.GetGenericTypeDefinition() == typeof(DbQuery<>))) +#pragma warning restore CS0618 // Type or member is obsolete + { + return NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(memberExpression.Type.GetGenericArguments()[0]); + } + + return base.VisitMember(memberExpression); + } + + protected override Expression VisitMethodCall(MethodCallExpression methodCallExpression) + { + if (methodCallExpression.Method.Name == nameof(DbContext.Set) + && methodCallExpression.Object != null + && typeof(DbContext).IsAssignableFrom(methodCallExpression.Object.Type) + && methodCallExpression.Type.IsGenericType + && methodCallExpression.Type.GetGenericTypeDefinition() == typeof(DbSet<>)) + { + return NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(methodCallExpression.Type.GetGenericArguments()[0]); + } + + return base.VisitMethodCall(methodCallExpression); + } + } + } +} diff --git a/src/EFCore/Properties/CoreStrings.Designer.cs b/src/EFCore/Properties/CoreStrings.Designer.cs index bb9b4496ab9..ab9577f7df6 100644 --- a/src/EFCore/Properties/CoreStrings.Designer.cs +++ b/src/EFCore/Properties/CoreStrings.Designer.cs @@ -2190,6 +2190,15 @@ public static string NoInterceptionResult public static string SetOperationWithDifferentIncludesInOperands => GetString("SetOperationWithDifferentIncludesInOperands"); + /// + /// Include is not supported for entities with defining query. Entity type: '{entityType}'. + /// + /// + public static string IncludeOnEntityWithDefiningQueryNotSupported([CanBeNull] object entityType) + => string.Format( + GetString("IncludeOnEntityWithDefiningQueryNotSupported", nameof(entityType)), + entityType); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/EFCore/Properties/CoreStrings.resx b/src/EFCore/Properties/CoreStrings.resx index 2528eba7177..5ca38bb7a2c 100644 --- a/src/EFCore/Properties/CoreStrings.resx +++ b/src/EFCore/Properties/CoreStrings.resx @@ -1204,4 +1204,7 @@ When performing a set operation, both operands must have the same Include operations. + + Include is not supported for entities with defining query. Entity type: '{entityType}' + diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index e84be40ebe9..d7521ae4375 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -22,6 +22,7 @@ public partial class NavigationExpandingExpressionVisitor : ExpressionVisitor private readonly ReducingExpressionVisitor _reducingExpressionVisitor; private readonly EntityReferenceOptionalMarkingExpressionVisitor _entityReferenceOptionalMarkingExpressionVisitor; private readonly ISet _parameterNames = new HashSet(); + private readonly EnumerableToQueryableMethodConvertingExpressionVisitor _enumerableToQueryableMethodConvertingExpressionVisitor; private readonly ParameterExtractingExpressionVisitor _parameterExtractingExpressionVisitor; private readonly Dictionary _parameterizedQueryFilterPredicateCache = new Dictionary(); @@ -40,6 +41,7 @@ public NavigationExpandingExpressionVisitor( _subqueryMemberPushdownExpressionVisitor = new SubqueryMemberPushdownExpressionVisitor(); _reducingExpressionVisitor = new ReducingExpressionVisitor(); _entityReferenceOptionalMarkingExpressionVisitor = new EntityReferenceOptionalMarkingExpressionVisitor(); + _enumerableToQueryableMethodConvertingExpressionVisitor = new EnumerableToQueryableMethodConvertingExpressionVisitor(); _parameterExtractingExpressionVisitor = new ParameterExtractingExpressionVisitor( evaluatableExpressionFilter, _parameters, @@ -65,10 +67,7 @@ private string GetParameterName(string prefix) public virtual Expression Expand(Expression query) { - var result = Visit(query); - result = _pendingSelectorExpandingExpressionVisitor.Visit(result); - result = new IncludeApplyingExpressionVisitor(this, _queryCompilationContext.IsTracking).Visit(result); - result = Reduce(result); + var result = ExpandAndReduce(query, applyInclude: true); var dbContextOnQueryContextPropertyAccess = Expression.Convert( @@ -97,6 +96,20 @@ public virtual Expression Expand(Expression query) return result; } + private Expression ExpandAndReduce(Expression query, bool applyInclude) + { + var result = Visit(query); + result = _pendingSelectorExpandingExpressionVisitor.Visit(result); + if (applyInclude) + { + result = new IncludeApplyingExpressionVisitor(this, _queryCompilationContext.IsTracking).Visit(result); + } + + result = Reduce(result); + + return result; + } + private static readonly PropertyInfo _queryContextContextPropertyInfo = typeof(QueryContext) .GetTypeInfo() @@ -106,7 +119,22 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio { if (constantExpression.IsEntityQueryable()) { - var navigationExpansionExpression = CreateNavigationExpansionExpression(constantExpression); + var entityType = _queryCompilationContext.Model.FindEntityType(((IQueryable)constantExpression.Value).ElementType); + var definingQuery = entityType.GetDefiningQuery(); + NavigationExpansionExpression navigationExpansionExpression; + if (definingQuery != null) + { + var processedDefiningQueryBody = _parameterExtractingExpressionVisitor.ExtractParameters(definingQuery.Body); + processedDefiningQueryBody = _enumerableToQueryableMethodConvertingExpressionVisitor.Visit(processedDefiningQueryBody); + navigationExpansionExpression = (NavigationExpansionExpression)Visit(processedDefiningQueryBody); + + var expanded = ExpandAndReduce(navigationExpansionExpression, applyInclude: false); + navigationExpansionExpression = CreateNavigationExpansionExpression(expanded, entityType); + } + else + { + navigationExpansionExpression = CreateNavigationExpansionExpression(constantExpression, entityType); + } return ApplyQueryFilter(navigationExpansionExpression); } @@ -114,16 +142,15 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio return base.VisitConstant(constantExpression); } - private NavigationExpansionExpression CreateNavigationExpansionExpression(ConstantExpression constantExpression) + private NavigationExpansionExpression CreateNavigationExpansionExpression(Expression sourceExpression, IEntityType entityType) { - var entityType = _queryCompilationContext.Model.FindEntityType(((IQueryable)constantExpression.Value).ElementType); var entityReference = new EntityReference(entityType); PopulateEagerLoadedNavigations(entityReference.IncludePaths); var currentTree = new NavigationTreeExpression(entityReference); var parameterName = GetParameterName(entityType.ShortName()[0].ToString().ToLower()); - return new NavigationExpansionExpression(constantExpression, currentTree, currentTree, parameterName); + return new NavigationExpansionExpression(sourceExpression, currentTree, currentTree, parameterName); } private Expression ApplyQueryFilter(NavigationExpansionExpression navigationExpansionExpression) @@ -132,13 +159,14 @@ private Expression ApplyQueryFilter(NavigationExpansionExpression navigationExpa { var entityType = _queryCompilationContext.Model.FindEntityType(navigationExpansionExpression.Type.GetSequenceType()); var rootEntityType = entityType.GetRootType(); - var queryFilterAnnotation = rootEntityType.FindAnnotation("QueryFilter"); - if (queryFilterAnnotation != null) + var queryFilter = rootEntityType.GetQueryFilter(); + if (queryFilter != null) { if (!_parameterizedQueryFilterPredicateCache.TryGetValue(rootEntityType, out var filterPredicate)) { - filterPredicate = (LambdaExpression)queryFilterAnnotation.Value; + filterPredicate = queryFilter; filterPredicate = (LambdaExpression)_parameterExtractingExpressionVisitor.ExtractParameters(filterPredicate); + filterPredicate = (LambdaExpression)_enumerableToQueryableMethodConvertingExpressionVisitor.Visit(filterPredicate); _parameterizedQueryFilterPredicateCache[rootEntityType] = filterPredicate; } @@ -556,10 +584,11 @@ when QueryableMethods.IsSumWithSelector(method): && methodCallExpression.Arguments.Count == 3 && methodCallExpression.Arguments[0] is ConstantExpression constantExpression && methodCallExpression.Arguments[1] is ConstantExpression - && methodCallExpression.Arguments[2] is ParameterExpression + && (methodCallExpression.Arguments[2] is ParameterExpression || methodCallExpression.Arguments[2] is ConstantExpression) && constantExpression.IsEntityQueryable()) { - var source = CreateNavigationExpansionExpression(constantExpression); + var entityType = _queryCompilationContext.Model.FindEntityType(((IQueryable)constantExpression.Value).ElementType); + var source = CreateNavigationExpansionExpression(constantExpression, entityType); source.UpdateSource( methodCallExpression.Update( null, @@ -623,6 +652,11 @@ private Expression ProcessInclude( if (source.PendingSelector is NavigationTreeExpression navigationTree && navigationTree.Value is EntityReference entityReferece) { + if (entityReferece.EntityType.GetDefiningQuery() != null) + { + throw new InvalidOperationException(CoreStrings.IncludeOnEntityWithDefiningQueryNotSupported(entityReferece.EntityType.DisplayName())); + } + if (expression is ConstantExpression includeConstant && includeConstant.Value is string navigationChain) { diff --git a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs index c443509b291..c6e813c53e6 100644 --- a/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/ParameterExtractingExpressionVisitor.cs @@ -59,8 +59,7 @@ public ParameterExtractingExpressionVisitor( _generateContextAccessors = generateContextAccessors; if (_generateContextAccessors) { - _contextParameterReplacingExpressionVisitor - = new ContextParameterReplacingExpressionVisitor(contextType); + _contextParameterReplacingExpressionVisitor = new ContextParameterReplacingExpressionVisitor(contextType); } } diff --git a/src/EFCore/Query/QueryTranslationPreprocessorDependencies.cs b/src/EFCore/Query/QueryTranslationPreprocessorDependencies.cs index 1b95192e8a7..20de5f247b5 100644 --- a/src/EFCore/Query/QueryTranslationPreprocessorDependencies.cs +++ b/src/EFCore/Query/QueryTranslationPreprocessorDependencies.cs @@ -3,7 +3,6 @@ using System.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Infrastructure; -using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; diff --git a/src/EFCore/Query/ReplacingExpressionVisitor.cs b/src/EFCore/Query/ReplacingExpressionVisitor.cs index 287d3910468..4d2c358e305 100644 --- a/src/EFCore/Query/ReplacingExpressionVisitor.cs +++ b/src/EFCore/Query/ReplacingExpressionVisitor.cs @@ -58,8 +58,11 @@ protected override Expression VisitMember(MemberExpression memberExpression) if (innerExpression is MemberInitExpression memberInitExpression) { - return ((MemberAssignment)memberInitExpression.Bindings - .Single(mb => mb.Member == memberExpression.Member)).Expression; + var matchingBinding = memberInitExpression.Bindings.Where(mb => mb.Member == memberExpression.Member).SingleOrDefault(); + if (matchingBinding != null) + { + return ((MemberAssignment)matchingBinding).Expression; + } } return memberExpression.Update(innerExpression); diff --git a/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.KeylessEntities.cs b/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.KeylessEntities.cs index fdf0dd5840b..40bdab7cff5 100644 --- a/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.KeylessEntities.cs +++ b/test/EFCore.Cosmos.FunctionalTests/Query/SimpleQueryCosmosTest.KeylessEntities.cs @@ -10,47 +10,46 @@ namespace Microsoft.EntityFrameworkCore.Query { public partial class SimpleQueryCosmosTest { - [ConditionalTheory(Skip = "See issue#13857")] + [ConditionalTheory] public override async Task KeylessEntity_simple(bool isAsync) { await base.KeylessEntity_simple(isAsync); AssertSql( - @"SELECT c + @"SELECT c[""Address""], c[""City""], c[""CompanyName""], c[""ContactName""], c[""ContactTitle""] FROM root c WHERE (c[""Discriminator""] = ""Customer"")"); } - [ConditionalTheory(Skip = "See issue#13857")] + [ConditionalTheory] public override async Task KeylessEntity_where_simple(bool isAsync) { await base.KeylessEntity_where_simple(isAsync); AssertSql( - @"SELECT c + @"SELECT c[""Address""], c[""City""], c[""CompanyName""], c[""ContactName""], c[""ContactTitle""] FROM root c -WHERE (c[""Discriminator""] = ""Customer"")"); +WHERE ((c[""Discriminator""] = ""Customer"") AND (c[""City""] = ""London""))"); } - [ConditionalFact(Skip = "See issue#13857")] + [ConditionalFact] public override void KeylessEntity_by_database_view() { base.KeylessEntity_by_database_view(); AssertSql( - @"SELECT c + @"SELECT c[""ProductID""], c[""ProductName""], ""Food"" AS CategoryName FROM root c WHERE ((c[""Discriminator""] = ""Product"") AND NOT(c[""Discontinued""]))"); } + [ConditionalFact(Skip = "issue #12086")] // collection support public override void KeylessEntity_with_nav_defining_query() { base.KeylessEntity_with_nav_defining_query(); AssertSql( - @"SELECT c -FROM root c -WHERE (c[""Discriminator""] = ""Customer"")"); + @""); } public override async Task KeylessEntity_with_mixed_tracking(bool isAsync) @@ -78,9 +77,9 @@ public override async Task KeylessEntity_with_defining_query(bool isAsync) await base.KeylessEntity_with_defining_query(isAsync); AssertSql( - @"SELECT c + @"SELECT c[""CustomerID""] FROM root c -WHERE (c[""Discriminator""] = ""Order"")"); +WHERE ((c[""Discriminator""] = ""Order"") AND (c[""CustomerID""] = ""ALFKI""))"); } public override async Task KeylessEntity_with_defining_query_and_correlated_collection(bool isAsync) @@ -93,36 +92,15 @@ FROM root c WHERE (c[""Discriminator""] = ""Customer"")"); } - public override async Task KeylessEntity_with_included_nav(bool isAsync) - { - await base.KeylessEntity_with_included_nav(isAsync); - - AssertSql( - @"SELECT c -FROM root c -WHERE (c[""Discriminator""] = ""Order"")"); - } - - public override async Task KeylessEntity_with_included_navs_multi_level(bool isAsync) - { - await base.KeylessEntity_with_included_navs_multi_level(isAsync); - - AssertSql( - @"SELECT c -FROM root c -WHERE (c[""Discriminator""] = ""Order"")"); - } - + [ConditionalTheory(Skip = "issue 312086")] // left join translation public override async Task KeylessEntity_select_where_navigation(bool isAsync) { await base.KeylessEntity_select_where_navigation(isAsync); - AssertSql( - @"SELECT c -FROM root c -WHERE (c[""Discriminator""] = ""Order"")"); + AssertSql(@""); } + [ConditionalTheory(Skip = "issue 312086")] // left join translation public override async Task KeylessEntity_select_where_navigation_multi_level(bool isAsync) { await AssertQuery( @@ -131,10 +109,7 @@ await AssertQuery( where ov.Customer.Orders.Any() select ov); - AssertSql( - @"SELECT c -FROM root c -WHERE (c[""Discriminator""] = ""Order"")"); + AssertSql(@""); } } } diff --git a/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs b/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs index f00b4675d26..797dddd616b 100644 --- a/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs +++ b/test/EFCore.Relational.Specification.Tests/Query/InheritanceRelationalTestBase.cs @@ -36,10 +36,6 @@ public virtual void FromSql_on_derived() } } - [ConditionalFact(Skip = "Issue #16323")] - public override void Can_query_all_animal_views() - => base.Can_query_all_animal_views(); - private string NormalizeDelimetersInRawString(string sql) => ((RelationalTestStore)Fixture.TestStore).NormalizeDelimetersInRawString(sql); diff --git a/test/EFCore.Relational.Specification.Tests/TestModels/Northwind/NorthwindRelationalContext.cs b/test/EFCore.Relational.Specification.Tests/TestModels/Northwind/NorthwindRelationalContext.cs index 9e3e346fecd..8e67f38ac4f 100644 --- a/test/EFCore.Relational.Specification.Tests/TestModels/Northwind/NorthwindRelationalContext.cs +++ b/test/EFCore.Relational.Specification.Tests/TestModels/Northwind/NorthwindRelationalContext.cs @@ -12,6 +12,8 @@ public NorthwindRelationalContext(DbContextOptions options) { } + public string _empty = string.Empty; + protected override void OnModelCreating(ModelBuilder modelBuilder) { base.OnModelCreating(modelBuilder); @@ -27,7 +29,8 @@ protected override void OnModelCreating(ModelBuilder modelBuilder) #pragma warning disable CS0618 // Type or member is obsolete modelBuilder.Query().HasNoKey().ToQuery( - () => CustomerQueries.FromSqlRaw("SELECT * FROM Customers")); + () => CustomerQueries.FromSqlInterpolated($"SELECT [c].[CustomerID] + {_empty} as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]" +)); modelBuilder .Query() diff --git a/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs b/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs index b565ca37c5c..72414b0be72 100644 --- a/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs @@ -330,7 +330,7 @@ public virtual void Extension_method_DbContext_property_chain_is_parameterized() } } - [ConditionalFact(Skip = "See issue#17111")] + [ConditionalFact] public virtual void Using_DbSet_in_filter_works() { using (var context = CreateContext()) @@ -339,7 +339,7 @@ public virtual void Using_DbSet_in_filter_works() } } - [ConditionalFact(Skip = "See issue#17111")] + [ConditionalFact] public virtual void Using_Context_set_method_in_filter_works() { using (var context = CreateContext()) diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.KeylessEntities.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.KeylessEntities.cs index 887bd9a9c98..721d07dad13 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.KeylessEntities.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.KeylessEntities.cs @@ -1,13 +1,11 @@ // 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 System.Collections.Generic; +using System; using System.Linq; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.TestModels.Northwind; -using Microsoft.EntityFrameworkCore.TestUtilities; -using Microsoft.EntityFrameworkCore.TestUtilities.Xunit; using Xunit; // ReSharper disable InconsistentNaming @@ -56,7 +54,7 @@ public virtual void Auto_initialized_view_set() } } - [ConditionalFact(Skip = "issue #16323")] + [ConditionalFact] public virtual void KeylessEntity_with_nav_defining_query() { using (var context = CreateContext()) @@ -70,7 +68,7 @@ var results } } - [ConditionalTheory(Skip = "issue #16323")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task KeylessEntity_with_defining_query(bool isAsync) { @@ -79,8 +77,7 @@ public virtual Task KeylessEntity_with_defining_query(bool isAsync) ovs => ovs.Where(ov => ov.CustomerID == "ALFKI")); } - // also issue 12873 - [ConditionalTheory(Skip = "issue #16323")] + [ConditionalTheory(Skip = "issue #12873")] [MemberData(nameof(IsAsyncData))] public virtual Task KeylessEntity_with_defining_query_and_correlated_collection(bool isAsync) { @@ -107,38 +104,53 @@ from o in ovs.Where(ov => ov.CustomerID == c.CustomerID) e => e.c.CustomerID); } - [ConditionalTheory(Skip = "issue #16323")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task KeylessEntity_with_included_nav(bool isAsync) + public virtual async Task KeylessEntity_with_included_nav(bool isAsync) { - return AssertIncludeQuery( - isAsync, - ovs => from ov in ovs.Include(ov => ov.Customer) - where ov.CustomerID == "ALFKI" - select ov, - new List + using (var ctx = CreateContext()) + { + if (isAsync) + { + await Assert.ThrowsAsync( + () => (from ov in ctx.Set().Include(ov => ov.Customer) + where ov.CustomerID == "ALFKI" + select ov).ToListAsync()); + } + else { - new ExpectedInclude(ov => ov.Customer, "Customer") - }); + await Assert.ThrowsAsync( + () => Task.FromResult((from ov in ctx.Set().Include(ov => ov.Customer) + where ov.CustomerID == "ALFKI" + select ov).ToList())); + } + } } - [ConditionalTheory(Skip = "issue #16323")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] - public virtual Task KeylessEntity_with_included_navs_multi_level(bool isAsync) + public virtual async Task KeylessEntity_with_included_navs_multi_level(bool isAsync) { - return AssertIncludeQuery( - isAsync, - ovs => from ov in ovs.Include(ov => ov.Customer.Orders) - where ov.CustomerID == "ALFKI" - select ov, - new List + using (var ctx = CreateContext()) + { + if (isAsync) + { + await Assert.ThrowsAsync( + () => (from ov in ctx.Set().Include(ov => ov.Customer.Orders) + where ov.CustomerID == "ALFKI" + select ov).ToListAsync()); + } + else { - new ExpectedInclude(ov => ov.Customer, "Customer"), - new ExpectedInclude(c => c.Orders, "Orders") - }); + await Assert.ThrowsAsync( + () => Task.FromResult((from ov in ctx.Set().Include(ov => ov.Customer.Orders) + where ov.CustomerID == "ALFKI" + select ov).ToList())); + } + } } - [ConditionalTheory(Skip = "issue #16323")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task KeylessEntity_select_where_navigation(bool isAsync) { @@ -149,7 +161,7 @@ public virtual Task KeylessEntity_select_where_navigation(bool isAsync) select ov); } - [ConditionalTheory(Skip = "issue #16323")] + [ConditionalTheory] [MemberData(nameof(IsAsyncData))] public virtual Task KeylessEntity_select_where_navigation_multi_level(bool isAsync) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs index 2ce143557e6..1ed5a655697 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/CompiledQuerySqlServerTest.cs @@ -1,6 +1,7 @@ // 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 System.Runtime.CompilerServices; using System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestUtilities; using Xunit; @@ -138,21 +139,53 @@ FROM [Customers] AS [c] WHERE [c].[CustomerID] = N'ALFKI'"); } - [ConditionalFact(Skip = "Issue #16323")] - public override Task DbQuery_query_async() - => base.DbQuery_query_async(); + [ConditionalFact] + public override async Task DbQuery_query_async() + { + await base.DbQuery_query_async(); + + AssertSql( + @"SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]", + // + @"SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]"); + } - [ConditionalFact(Skip = "Issue #16323")] + [ConditionalFact] public override void DbQuery_query_first() - => base.DbQuery_query_first(); + { + base.DbQuery_query_first(); + + AssertSql( + @"SELECT TOP(1) [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle] +FROM ( + SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] +) AS [c] +ORDER BY [c].[CompanyName]"); + } - [ConditionalFact(Skip = "Issue #16323")] - public override Task DbQuery_query_first_async() - => base.DbQuery_query_first_async(); + [ConditionalFact] + public override async Task DbQuery_query_first_async() + { + await base.DbQuery_query_first_async(); - [ConditionalFact(Skip = "Issue #16323")] + AssertSql( + @"SELECT TOP(1) [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle] +FROM ( + SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] +) AS [c] +ORDER BY [c].[CompanyName]"); + } + + [ConditionalFact] public override void DbQuery_query() - => base.DbQuery_query(); + { + base.DbQuery_query(); + + AssertSql( + @"SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]", + // + @"SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]"); + } private void AssertSql(params string[] expected) => Fixture.TestSqlLoggerFactory.AssertBaseline(expected); diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs index 9516eccc072..82278de3a8c 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/InheritanceSqlServerTest.cs @@ -218,7 +218,9 @@ public override void Can_query_all_animal_views() AssertSql( @"SELECT [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] -FROM [Animal] AS [a] +FROM ( + SELECT * FROM Animal +) AS [a] WHERE [a].[Discriminator] IN (N'Eagle', N'Kiwi') ORDER BY [a].[CountryId]"); } diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index cf4e358ac64..91e5443edf9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -5316,7 +5316,7 @@ public class AddressTurnovers13157 #region Bug13346 - [ConditionalFact(Skip = "Issue #16323")] + [ConditionalFact] public virtual void ToQuery_can_define_in_own_terms_using_FromSql() { using (CreateDatabase13346()) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs index 076e1cb05de..59451e7d4b5 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs @@ -348,12 +348,17 @@ public override void Using_DbSet_in_filter_works() base.Using_DbSet_in_filter_works(); AssertSql( - @"SELECT [p].[Id], [p].[Filler] + @"@__ef_filter__Property_0='False' + +SELECT [p].[Id], [p].[Filler] FROM [PrincipalSetFilter] AS [p] WHERE EXISTS ( SELECT 1 FROM [Dependents] AS [d] - WHERE [d].[PrincipalSetFilterId] = [p].[Id])"); + WHERE EXISTS ( + SELECT 1 + FROM [MultiContextFilter] AS [m] + WHERE ((([m].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND ([m].[BossId] = 1)) AND ([m].[BossId] = [d].[PrincipalSetFilterId])) AND ([d].[PrincipalSetFilterId] = [p].[Id]))"); } public override void Using_Context_set_method_in_filter_works() @@ -361,12 +366,14 @@ public override void Using_Context_set_method_in_filter_works() base.Using_Context_set_method_in_filter_works(); AssertSql( - @"SELECT [p].[Id], [p].[PrincipalSetFilterId] -FROM [Dependents] AS [p] + @"@__ef_filter__Property_0='False' + +SELECT [d].[Id], [d].[PrincipalSetFilterId] +FROM [Dependents] AS [d] WHERE EXISTS ( SELECT 1 - FROM [MultiContextFilter] AS [b] - WHERE [b].[BossId] = [p].[PrincipalSetFilterId])"); + FROM [MultiContextFilter] AS [m] + WHERE ((([m].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND ([m].[BossId] = 1)) AND ([m].[BossId] = [d].[PrincipalSetFilterId]))"); } public override void Static_member_from_dbContext_is_inlined() diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.KeylessEntities.cs b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.KeylessEntities.cs index 1d0ec730de5..324bc24390a 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.KeylessEntities.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/SimpleQuerySqlServerTest.KeylessEntities.cs @@ -8,24 +8,25 @@ namespace Microsoft.EntityFrameworkCore.Query { public partial class SimpleQuerySqlServerTest { - [ConditionalTheory(Skip = "Issue #16323")] + [ConditionalTheory] public override async Task KeylessEntity_simple(bool isAsync) { await base.KeylessEntity_simple(isAsync); AssertSql( - @"SELECT [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle] -FROM [Customers] AS [c]"); + @"SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]"); } - [ConditionalTheory(Skip = "Issue #16323")] + [ConditionalTheory] public override async Task KeylessEntity_where_simple(bool isAsync) { await base.KeylessEntity_where_simple(isAsync); AssertSql( @"SELECT [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle] -FROM [Customers] AS [c] +FROM ( + SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c] +) AS [c] WHERE ([c].[City] = N'London') AND [c].[City] IS NOT NULL"); } @@ -33,9 +34,14 @@ public override void KeylessEntity_by_database_view() { base.KeylessEntity_by_database_view(); + // when we have defining query and ToView, defining query wins + // AssertSql( + // @"SELECT [a].[CategoryName], [a].[ProductID], [a].[ProductName] + //FROM [Alphabetical list of products] AS [a]"); AssertSql( - @"SELECT [a].[CategoryName], [a].[ProductID], [a].[ProductName] -FROM [Alphabetical list of products] AS [a]"); + @"SELECT [p].[ProductID], [p].[ProductName], N'Food' AS [CategoryName] +FROM [Products] AS [p] +WHERE [p].[Discontinued] <> CAST(1 AS bit)"); } public override void KeylessEntity_with_nav_defining_query() @@ -46,16 +52,15 @@ public override void KeylessEntity_with_nav_defining_query() @"@__ef_filter___searchTerm_0='A' (Size = 4000) @__ef_filter___searchTerm_1='A' (Size = 4000) -SELECT [t].[CompanyName], [t].[OrderCount], [t].[SearchTerm] -FROM ( - SELECT [c].[CompanyName], ( - SELECT COUNT(*) - FROM [Orders] AS [o] - WHERE [c].[CustomerID] = [o].[CustomerID] - ) AS [OrderCount], @__ef_filter___searchTerm_0 AS [SearchTerm] - FROM [Customers] AS [c] -) AS [t] -WHERE (([t].[CompanyName] LIKE @__ef_filter___searchTerm_1 + N'%' AND (LEFT([t].[CompanyName], LEN(@__ef_filter___searchTerm_1)) = @__ef_filter___searchTerm_1)) OR (@__ef_filter___searchTerm_1 = N'')) AND ([t].[OrderCount] > 0)"); +SELECT [c].[CompanyName], ( + SELECT COUNT(*) + FROM [Orders] AS [o] + WHERE ([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL) AS [OrderCount], @__ef_filter___searchTerm_0 AS [SearchTerm] +FROM [Customers] AS [c] +WHERE (((@__ef_filter___searchTerm_1 = N'') AND @__ef_filter___searchTerm_1 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter___searchTerm_1 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter___searchTerm_1)) = @__ef_filter___searchTerm_1) AND (LEFT([c].[CompanyName], LEN(@__ef_filter___searchTerm_1)) IS NOT NULL AND @__ef_filter___searchTerm_1 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter___searchTerm_1)) IS NULL AND @__ef_filter___searchTerm_1 IS NULL)))))) AND (( + SELECT COUNT(*) + FROM [Orders] AS [o] + WHERE ([c].[CustomerID] = [o].[CustomerID]) AND [o].[CustomerID] IS NOT NULL) > 0)"); } public override async Task KeylessEntity_with_mixed_tracking(bool isAsync) @@ -79,14 +84,11 @@ public override async Task KeylessEntity_with_defining_query(bool isAsync) await base.KeylessEntity_with_defining_query(isAsync); AssertSql( - @"SELECT [t].[CustomerID] + @"SELECT [o].[CustomerID] FROM ( - SELECT [o].[CustomerID] - FROM ( - select * from ""Orders"" - ) AS [o] -) AS [t] -WHERE [t].[CustomerID] = N'ALFKI'"); + select * from ""Orders"" +) AS [o] +WHERE ([o].[CustomerID] = N'ALFKI') AND [o].[CustomerID] IS NOT NULL"); } public override async Task KeylessEntity_with_defining_query_and_correlated_collection(bool isAsync) @@ -97,68 +99,17 @@ public override async Task KeylessEntity_with_defining_query_and_correlated_coll ""); } - public override async Task KeylessEntity_with_included_nav(bool isAsync) - { - await base.KeylessEntity_with_included_nav(isAsync); - - AssertSql( - @"SELECT [t].[CustomerID], [ov.Customer].[CustomerID], [ov.Customer].[Address], [ov.Customer].[City], [ov.Customer].[CompanyName], [ov.Customer].[ContactName], [ov.Customer].[ContactTitle], [ov.Customer].[Country], [ov.Customer].[Fax], [ov.Customer].[Phone], [ov.Customer].[PostalCode], [ov.Customer].[Region] -FROM ( - SELECT [o].[CustomerID] - FROM ( - select * from ""Orders"" - ) AS [o] -) AS [t] -LEFT JOIN [Customers] AS [ov.Customer] ON [t].[CustomerID] = [ov.Customer].[CustomerID] -WHERE [t].[CustomerID] = N'ALFKI'"); - } - - public override async Task KeylessEntity_with_included_navs_multi_level(bool isAsync) - { - await base.KeylessEntity_with_included_navs_multi_level(isAsync); - - AssertSql( - @"SELECT [t].[CustomerID], [ov.Customer].[CustomerID], [ov.Customer].[Address], [ov.Customer].[City], [ov.Customer].[CompanyName], [ov.Customer].[ContactName], [ov.Customer].[ContactTitle], [ov.Customer].[Country], [ov.Customer].[Fax], [ov.Customer].[Phone], [ov.Customer].[PostalCode], [ov.Customer].[Region] -FROM ( - SELECT [o].[CustomerID] - FROM ( - select * from ""Orders"" - ) AS [o] -) AS [t] -LEFT JOIN [Customers] AS [ov.Customer] ON [t].[CustomerID] = [ov.Customer].[CustomerID] -WHERE [t].[CustomerID] = N'ALFKI' -ORDER BY [ov.Customer].[CustomerID]", - // - @"SELECT [ov.Customer.Orders].[OrderID], [ov.Customer.Orders].[CustomerID], [ov.Customer.Orders].[EmployeeID], [ov.Customer.Orders].[OrderDate] -FROM [Orders] AS [ov.Customer.Orders] -INNER JOIN ( - SELECT DISTINCT [t0].[CustomerID], [ov.Customer0].[CustomerID] AS [CustomerID0] - FROM ( - SELECT [o0].[CustomerID] - FROM ( - select * from ""Orders"" - ) AS [o0] - ) AS [t0] - LEFT JOIN [Customers] AS [ov.Customer0] ON [t0].[CustomerID] = [ov.Customer0].[CustomerID] - WHERE [t0].[CustomerID] = N'ALFKI' -) AS [t1] ON [ov.Customer.Orders].[CustomerID] = [t1].[CustomerID] -ORDER BY [t1].[CustomerID]"); - } - public override async Task KeylessEntity_select_where_navigation(bool isAsync) { await base.KeylessEntity_select_where_navigation(isAsync); AssertSql( - @"SELECT [t].[CustomerID] + @"SELECT [o].[CustomerID] FROM ( - SELECT [o].[CustomerID] - FROM ( - select * from ""Orders"" - ) AS [o] -) AS [t] -LEFT JOIN [Customers] AS [ov.Customer] ON [t].[CustomerID] = [ov.Customer].[CustomerID] -WHERE [ov.Customer].[City] = N'Seattle'"); + select * from ""Orders"" +) AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE ([c].[City] = N'Seattle') AND [c].[City] IS NOT NULL"); } public override async Task KeylessEntity_select_where_navigation_multi_level(bool isAsync) @@ -166,22 +117,24 @@ public override async Task KeylessEntity_select_where_navigation_multi_level(boo await base.KeylessEntity_select_where_navigation_multi_level(isAsync); AssertSql( - @"SELECT [t].[CustomerID] + @"SELECT [o].[CustomerID] FROM ( - SELECT [o].[CustomerID] - FROM ( - select * from ""Orders"" - ) AS [o] -) AS [t] -LEFT JOIN [Customers] AS [ov.Customer] ON [t].[CustomerID] = [ov.Customer].[CustomerID] + select * from ""Orders"" +) AS [o] +LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] WHERE EXISTS ( SELECT 1 FROM [Orders] AS [o0] - WHERE [ov.Customer].[CustomerID] = [o0].[CustomerID])"); + WHERE (([c].[CustomerID] = [o0].[CustomerID]) AND ([c].[CustomerID] IS NOT NULL AND [o0].[CustomerID] IS NOT NULL)) OR ([c].[CustomerID] IS NULL AND [o0].[CustomerID] IS NULL))"); } - [ConditionalFact(Skip = "Issue #16323")] + [ConditionalFact] public override void Auto_initialized_view_set() - => base.Auto_initialized_view_set(); + { + base.Auto_initialized_view_set(); + + AssertSql( + @"SELECT [c].[CustomerID] + N'' as [CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] FROM [Customers] AS [c]"); + } } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs index ced708eb315..a1e28de4da0 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/CompiledQueryInMemoryTest.cs @@ -1,9 +1,7 @@ // 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 System.Threading.Tasks; using Microsoft.EntityFrameworkCore.TestUtilities; -using Xunit; namespace Microsoft.EntityFrameworkCore.Query { @@ -13,21 +11,5 @@ public CompiledQuerySqliteTest(NorthwindQuerySqliteFixture : base(fixture) { } - - [ConditionalFact(Skip = "Issue #16323")] - public override Task DbQuery_query_async() - => base.DbQuery_query_async(); - - [ConditionalFact(Skip = "Issue #16323")] - public override void DbQuery_query_first() - => base.DbQuery_query_first(); - - [ConditionalFact(Skip = "Issue #16323")] - public override Task DbQuery_query_first_async() - => base.DbQuery_query_first_async(); - - [ConditionalFact(Skip = "Issue #16323")] - public override void DbQuery_query() - => base.DbQuery_query(); } } diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs index 0b91a2df5f5..916bc421bed 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/SimpleQuerySqliteTest.cs @@ -1309,18 +1309,6 @@ LIMIT @__p_0 public override Task Project_single_element_from_collection_with_multiple_OrderBys_Take_and_FirstOrDefault_2(bool isAsync) => base.Project_single_element_from_collection_with_multiple_OrderBys_Take_and_FirstOrDefault_2(isAsync); - [ConditionalFact(Skip = "Issue #16323")] - public override void Auto_initialized_view_set() - => base.Auto_initialized_view_set(); - - [ConditionalTheory(Skip = "Issue #16323")] - public override Task KeylessEntity_simple(bool isAsync) - => base.KeylessEntity_simple(isAsync); - - [ConditionalTheory(Skip = "Issue #16323")] - public override Task KeylessEntity_where_simple(bool isAsync) - => base.KeylessEntity_where_simple(isAsync); - // Sqlite does not support lateral joins public override void Select_nested_collection_multi_level() { diff --git a/test/EFCore.Sqlite.FunctionalTests/WithConstructorsSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/WithConstructorsSqliteTest.cs index 6f64c3cac36..edbe63dd83c 100644 --- a/test/EFCore.Sqlite.FunctionalTests/WithConstructorsSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/WithConstructorsSqliteTest.cs @@ -4,7 +4,6 @@ using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Storage; using Microsoft.EntityFrameworkCore.TestUtilities; -using Xunit; namespace Microsoft.EntityFrameworkCore { @@ -15,10 +14,6 @@ public WithConstructorsSqliteTest(WithConstructorsSqliteFixture fixture) { } - [ConditionalFact(Skip = "Issue #16323")] - public override void Query_with_keyless_type() - => base.Query_with_keyless_type(); - protected override void UseTransaction(DatabaseFacade facade, IDbContextTransaction transaction) => facade.UseTransaction(transaction.GetDbTransaction());