diff --git a/src/EFCore/Internal/EntityFinder.cs b/src/EFCore/Internal/EntityFinder.cs index c76ffaf6d48..70d6ba9570a 100644 --- a/src/EFCore/Internal/EntityFinder.cs +++ b/src/EFCore/Internal/EntityFinder.cs @@ -222,7 +222,7 @@ private IQueryable GetDatabaseValuesQuery(InternalEntityEntry entry) keyValues[i] = keyValue; } - return _queryRoot.AsNoTracking()//.IgnoreQueryFilters() + return _queryRoot.AsNoTracking().IgnoreQueryFilters() .Where(BuildObjectLambda(properties, new ValueBuffer(keyValues))) .Select(BuildProjection(entityType)); } diff --git a/src/EFCore/Query/NavigationExpansion/MaterializeCollectionNavigationExpression.cs b/src/EFCore/Query/NavigationExpansion/MaterializeCollectionNavigationExpression.cs new file mode 100644 index 00000000000..330d8eb38d0 --- /dev/null +++ b/src/EFCore/Query/NavigationExpansion/MaterializeCollectionNavigationExpression.cs @@ -0,0 +1,48 @@ +// 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.Linq.Expressions; +using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; +using Microsoft.EntityFrameworkCore.Query.Internal; + +namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion +{ + public class MaterializeCollectionNavigationExpression : Expression, IPrintable + { + private Type _returnType; + public MaterializeCollectionNavigationExpression(Expression operand, INavigation navigation) + { + Operand = operand; + Navigation = navigation; + _returnType = navigation.ClrType; + } + + public virtual Expression Operand { get; } + public virtual INavigation Navigation { get; } + + public override ExpressionType NodeType => ExpressionType.Extension; + public override Type Type => _returnType; + public override bool CanReduce => false; + + protected override Expression VisitChildren(ExpressionVisitor visitor) + { + var newOperand = visitor.Visit(Operand); + + return Update(newOperand); + } + + public virtual MaterializeCollectionNavigationExpression Update(Expression operand) + => operand != Operand + ? new MaterializeCollectionNavigationExpression(operand, Navigation) + : this; + + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.StringBuilder.Append($"MATERIALIZE_COLLECTION({ Navigation }, "); + expressionPrinter.Visit(Operand); + expressionPrinter.StringBuilder.Append(")"); + } + } +} diff --git a/src/EFCore/Query/NavigationExpansion/NavigationExpander.cs b/src/EFCore/Query/NavigationExpansion/NavigationExpander.cs index 6b4335deed4..e43cfcfc2e3 100644 --- a/src/EFCore/Query/NavigationExpansion/NavigationExpander.cs +++ b/src/EFCore/Query/NavigationExpansion/NavigationExpander.cs @@ -3,27 +3,28 @@ using System.Linq.Expressions; using JetBrains.Annotations; -using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors; +using Microsoft.EntityFrameworkCore.Query.Pipeline; using Microsoft.EntityFrameworkCore.Utilities; namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion { public class NavigationExpander { - private IModel _model; + private readonly QueryCompilationContext _queryCompilationContext; - public NavigationExpander([NotNull] IModel model) + public NavigationExpander([NotNull] QueryCompilationContext queryCompilationContext) { - Check.NotNull(model, nameof(model)); + Check.NotNull(queryCompilationContext, nameof(queryCompilationContext)); - _model = model; + _queryCompilationContext = queryCompilationContext; } public virtual Expression ExpandNavigations(Expression expression) { - var newExpression = new NavigationExpandingVisitor(_model).Visit(expression); - newExpression = new NavigationExpansionReducingVisitor().Visit(newExpression); + var navigationExpandingVisitor = new NavigationExpandingVisitor(_queryCompilationContext); + var newExpression = navigationExpandingVisitor.Visit(expression); + newExpression = new NavigationExpansionReducingVisitor(navigationExpandingVisitor, _queryCompilationContext).Visit(newExpression); return newExpression; } diff --git a/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs b/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs index 14552be1632..a4db124c10f 100644 --- a/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs +++ b/src/EFCore/Query/NavigationExpansion/NavigationExpansionHelpers.cs @@ -11,6 +11,8 @@ using Microsoft.EntityFrameworkCore.Metadata.Internal; using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors; +using Microsoft.EntityFrameworkCore.Query.Pipeline; namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion { @@ -19,7 +21,9 @@ public static class NavigationExpansionHelpers public static NavigationExpansionExpression CreateNavigationExpansionRoot( Expression operand, IEntityType entityType, - INavigation materializeCollectionNavigation) + INavigation materializeCollectionNavigation, + NavigationExpandingVisitor navigationExpandingVisitor, + QueryCompilationContext queryCompilationContext) { var sourceMapping = new SourceMapping { @@ -39,7 +43,7 @@ public static NavigationExpansionExpression CreateNavigationExpansionRoot( pendingSelectorParameter.Type), pendingSelectorParameter); - return new NavigationExpansionExpression( + var result = new NavigationExpansionExpression( operand, new NavigationExpansionExpressionState( pendingSelectorParameter, @@ -53,6 +57,49 @@ public static NavigationExpansionExpression CreateNavigationExpansionRoot( customRootMappings: new List>(), materializeCollectionNavigation), materializeCollectionNavigation?.ClrType ?? operand.Type); + + var rootEntityType = entityType.RootType(); + var queryFilterAnnotation = rootEntityType.FindAnnotation("QueryFilter"); + if (queryFilterAnnotation != null + && !queryCompilationContext.IgnoreQueryFilters + && !navigationExpandingVisitor.AppliedQueryFilters.Contains(rootEntityType)) + { + navigationExpandingVisitor.AppliedQueryFilters.Add(rootEntityType); + var filterPredicate = (LambdaExpression)queryFilterAnnotation.Value; + + var parameterExtractingExpressionVisitor = new ParameterExtractingExpressionVisitor( + queryCompilationContext.EvaluatableExpressionFilter, + queryCompilationContext.ParameterValues, + queryCompilationContext.ContextType, + queryCompilationContext.Logger, + parameterize: false, + generateContextAccessors: true); + + filterPredicate = (LambdaExpression)parameterExtractingExpressionVisitor.ExtractParameters(filterPredicate); + + // in case of query filters we need to strip MaterializeCollectionNavigation from the initial collection and apply it after the filter + result = (NavigationExpansionExpression)RemoveMaterializeCollection(result); + var sequenceType = result.Type.GetSequenceType(); + + // if we are constructing EntityQueryable of a derived type, we need to re-map filter predicate to the correct derived type + var filterPredicateParameter = filterPredicate.Parameters[0]; + if (filterPredicateParameter.Type != sequenceType) + { + var newFilterPredicateParameter = Expression.Parameter(sequenceType, filterPredicateParameter.Name); + filterPredicate = (LambdaExpression)new ExpressionReplacingVisitor(filterPredicateParameter, newFilterPredicateParameter).Visit(filterPredicate); + } + + var whereMethod = LinqMethodHelpers.QueryableWhereMethodInfo.MakeGenericMethod(result.Type.GetSequenceType()); + var filteredResult = (Expression)Expression.Call(whereMethod, result, filterPredicate); + if (materializeCollectionNavigation != null) + { + filteredResult = new MaterializeCollectionNavigationExpression(result, materializeCollectionNavigation); + } + + result = (NavigationExpansionExpression)navigationExpandingVisitor.Visit(filteredResult); + } + + return result; } private static readonly MethodInfo _leftJoinMethodInfo = typeof(QueryableExtensions).GetTypeInfo() @@ -65,7 +112,9 @@ public static (Expression source, ParameterExpression parameter) AddNavigationJo NavigationTreeNode navigationTree, NavigationExpansionExpressionState state, List navigationPath, - bool include) + bool include, + NavigationExpandingVisitor navigationExpandingVisitor, + QueryCompilationContext queryCompilationContext) { var joinNeeded = include ? navigationTree.Included == NavigationTreeNodeIncludeMode.ReferencePending @@ -73,140 +122,172 @@ public static (Expression source, ParameterExpression parameter) AddNavigationJo if (joinNeeded) { - var navigation = navigationTree.Navigation; - var sourceType = sourceExpression.Type.GetSequenceType(); - var navigationTargetEntityType = navigation.GetTargetType(); - - var entityQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(navigationTargetEntityType.ClrType); - var resultType = typeof(TransparentIdentifier<,>).MakeGenericType(sourceType, navigationTargetEntityType.ClrType); - - var outerParameter = Expression.Parameter(sourceType, parameterExpression.Name); - var outerKeySelectorParameter = outerParameter; - var transparentIdentifierAccessorExpression = outerParameter.BuildPropertyAccess(navigationTree.Parent.ToMapping); - - var outerKeySelectorBody = CreateKeyAccessExpression( - transparentIdentifierAccessorExpression, - navigation.IsDependentToPrincipal() - ? navigation.ForeignKey.Properties - : navigation.ForeignKey.PrincipalKey.Properties, - addNullCheck: navigationTree.Parent != null && navigationTree.Parent.Optional); - - var innerKeySelectorParameterType = navigationTargetEntityType.ClrType; - var innerKeySelectorParameter = Expression.Parameter( - innerKeySelectorParameterType, - parameterExpression.Name + "." + navigationTree.Navigation.Name); - - var innerKeySelectorBody = CreateKeyAccessExpression( - innerKeySelectorParameter, - navigation.IsDependentToPrincipal() - ? navigation.ForeignKey.PrincipalKey.Properties - : navigation.ForeignKey.Properties); - - if (outerKeySelectorBody.Type.IsNullableType() - && !innerKeySelectorBody.Type.IsNullableType()) + // TODO: hack/quirk to work-around potential bugs in the new navigation generation + if (queryCompilationContext.IgnoreQueryFilters) { - innerKeySelectorBody = Expression.Convert(innerKeySelectorBody, outerKeySelectorBody.Type); + LegacyCodepath(ref sourceExpression, ref parameterExpression, navigationTree, state, navigationPath, include); } - else if (innerKeySelectorBody.Type.IsNullableType() - && !outerKeySelectorBody.Type.IsNullableType()) + else { - outerKeySelectorBody = Expression.Convert(outerKeySelectorBody, innerKeySelectorBody.Type); - } + var navigation = navigationTree.Navigation; + var sourceType = sourceExpression.Type.GetSequenceType(); + var entityQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(navigation.GetTargetType().ClrType); + var navigationRoot = CreateNavigationExpansionRoot(entityQueryable, navigation.GetTargetType(), materializeCollectionNavigation: null, navigationExpandingVisitor, queryCompilationContext); + + var resultType = typeof(TransparentIdentifier<,>).MakeGenericType(sourceType, navigationRoot.State.CurrentParameter.Type); + + var outerParameter = Expression.Parameter(sourceType, parameterExpression.Name); + var outerKeySelectorParameter = outerParameter; + var outerTransparentIdentifierAccessorExpression = outerParameter.BuildPropertyAccess(navigationTree.Parent.ToMapping); + + var outerKeySelectorBody = CreateKeyAccessExpression( + outerTransparentIdentifierAccessorExpression, + navigation.IsDependentToPrincipal() + ? navigation.ForeignKey.Properties + : navigation.ForeignKey.PrincipalKey.Properties, + addNullCheck: navigationTree.Parent != null && navigationTree.Parent.Optional); + + var innerKeySelectorParameter = Expression.Parameter( + navigationRoot.State.CurrentParameter.Type, + navigationRoot.State.CurrentParameter.Name + "." + navigationTree.Navigation.Name); + + // we are guaranteed to only have one SourceMapping here because it will either be a simple EntityQueryable (normal navigation expansion case) + // or EntityQueryable with navigations from query filters - those however can only contain navigations that stem from the original, so it's all part of the same root, hence only one SourceMapping + // + // if the query filter is complex and contains Joins/GroupJoins etc that would spawn additional SourceMappings, + // those mappings would be on the inner NavigationExpansionExpression, so we don't need to worry about them here. + var navigationRootSourceMapping = navigationRoot.State.SourceMappings.Single(); + var innerTransparentIdentifierAccessorExpression = innerKeySelectorParameter.BuildPropertyAccess(navigationRootSourceMapping.NavigationTree.ToMapping); + + var innerKeySelectorBody = CreateKeyAccessExpression( + innerTransparentIdentifierAccessorExpression, + navigation.IsDependentToPrincipal() + ? navigation.ForeignKey.PrincipalKey.Properties + : navigation.ForeignKey.Properties); + + if (outerKeySelectorBody.Type.IsNullableType() + && !innerKeySelectorBody.Type.IsNullableType()) + { + innerKeySelectorBody = Expression.Convert(innerKeySelectorBody, outerKeySelectorBody.Type); + } + else if (innerKeySelectorBody.Type.IsNullableType() + && !outerKeySelectorBody.Type.IsNullableType()) + { + outerKeySelectorBody = Expression.Convert(outerKeySelectorBody, innerKeySelectorBody.Type); + } - var outerKeySelector = Expression.Lambda( - outerKeySelectorBody, - outerKeySelectorParameter); + var outerKeySelector = Expression.Lambda( + outerKeySelectorBody, + outerKeySelectorParameter); - var innerKeySelector = Expression.Lambda( - innerKeySelectorBody, - innerKeySelectorParameter); + var innerKeySelector = Expression.Lambda( + innerKeySelectorBody, + innerKeySelectorParameter); - if (!sourceExpression.Type.IsQueryableType()) - { - var asQueryableMethodInfo = LinqMethodHelpers.AsQueryable.MakeGenericMethod(sourceType); - sourceExpression = Expression.Call(asQueryableMethodInfo, sourceExpression); - } + if (!sourceExpression.Type.IsQueryableType()) + { + var asQueryableMethodInfo = LinqMethodHelpers.AsQueryable.MakeGenericMethod(sourceType); + sourceExpression = Expression.Call(asQueryableMethodInfo, sourceExpression); + } - var joinMethodInfo = navigationTree.Optional - ? _leftJoinMethodInfo.MakeGenericMethod( - sourceType, - navigationTargetEntityType.ClrType, - outerKeySelector.Body.Type, - resultType) - : LinqMethodHelpers.QueryableJoinMethodInfo.MakeGenericMethod( - sourceType, - navigationTargetEntityType.ClrType, - outerKeySelector.Body.Type, - resultType); - - var resultSelectorOuterParameterName = outerParameter.Name; - var resultSelectorOuterParameter = Expression.Parameter(sourceType, resultSelectorOuterParameterName); - - var resultSelectorInnerParameterName = innerKeySelectorParameter.Name; - var resultSelectorInnerParameter = Expression.Parameter(navigationTargetEntityType.ClrType, resultSelectorInnerParameterName); - - var transparentIdentifierCtorInfo - = resultType.GetTypeInfo().GetConstructors().Single(); - - var transparentIdentifierOuterMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Outer"); - var transparentIdentifierInnerMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Inner"); - - var resultSelector = Expression.Lambda( - Expression.New( - transparentIdentifierCtorInfo, - new[] { resultSelectorOuterParameter, resultSelectorInnerParameter }, - new[] { transparentIdentifierOuterMemberInfo, transparentIdentifierInnerMemberInfo }), - resultSelectorOuterParameter, - resultSelectorInnerParameter); - - var joinMethodCall = Expression.Call( - joinMethodInfo, - sourceExpression, - entityQueryable, - outerKeySelector, - innerKeySelector, - resultSelector); - - sourceExpression = joinMethodCall; - - var transparentIdentifierParameterName = resultSelectorInnerParameterName; - var transparentIdentifierParameter = Expression.Parameter(resultSelector.ReturnType, transparentIdentifierParameterName); - parameterExpression = transparentIdentifierParameter; - - // remap navigation 'To' paths -> for this navigation prepend "Inner", for every other (already expanded) navigation prepend "Outer" - navigationTree.ToMapping.Insert(0, nameof(TransparentIdentifier.Inner)); - foreach (var mapping in state.SourceMappings) - { - var nodes = include - ? mapping.NavigationTree.Flatten().Where(n => (n.Included == NavigationTreeNodeIncludeMode.ReferenceComplete + var joinMethodInfo = navigationTree.Optional + ? _leftJoinMethodInfo.MakeGenericMethod( + sourceType, + navigationRoot.State.CurrentParameter.Type, + outerKeySelector.Body.Type, + resultType) + : LinqMethodHelpers.QueryableJoinMethodInfo.MakeGenericMethod( + sourceType, + navigationRoot.State.CurrentParameter.Type, + outerKeySelector.Body.Type, + resultType); + + var resultSelectorOuterParameterName = outerParameter.Name; + var resultSelectorOuterParameter = Expression.Parameter(sourceType, resultSelectorOuterParameterName); + + var resultSelectorInnerParameterName = innerKeySelectorParameter.Name; + var resultSelectorInnerParameter = Expression.Parameter(navigationRoot.State.CurrentParameter.Type, resultSelectorInnerParameterName); + + var transparentIdentifierCtorInfo + = resultType.GetTypeInfo().GetConstructors().Single(); + + var transparentIdentifierOuterMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Outer"); + var transparentIdentifierInnerMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Inner"); + + var resultSelector = Expression.Lambda( + Expression.New( + transparentIdentifierCtorInfo, + new[] { resultSelectorOuterParameter, resultSelectorInnerParameter }, + new[] { transparentIdentifierOuterMemberInfo, transparentIdentifierInnerMemberInfo }), + resultSelectorOuterParameter, + resultSelectorInnerParameter); + + var joinMethodCall = Expression.Call( + joinMethodInfo, + sourceExpression, + navigationRoot.Operand, + outerKeySelector, + innerKeySelector, + resultSelector); + + sourceExpression = joinMethodCall; + + var transparentIdentifierParameterName = resultSelectorInnerParameterName; + var transparentIdentifierParameter = Expression.Parameter(resultSelector.ReturnType, transparentIdentifierParameterName); + parameterExpression = transparentIdentifierParameter; + + // remap navigation 'To' paths -> for inner navigations (they should all be expanded by now) prepend "Inner", for every other (already expanded) navigation prepend "Outer" + var innerNodes = include + ? navigationRootSourceMapping.NavigationTree.Flatten().Where(n => (n.Included == NavigationTreeNodeIncludeMode.ReferenceComplete || n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete || n.Navigation.ForeignKey.IsOwnership) && n != navigationTree) - : mapping.NavigationTree.Flatten().Where(n => (n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete + : navigationRootSourceMapping.NavigationTree.Flatten().Where(n => (n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete || n.Navigation.ForeignKey.IsOwnership) && n != navigationTree); - foreach (var navigationTreeNode in nodes) + foreach (var innerNode in innerNodes) { - navigationTreeNode.ToMapping.Insert(0, nameof(TransparentIdentifier.Outer)); + innerNode.ToMapping.Insert(0, nameof(TransparentIdentifier.Inner)); } - } - foreach (var customRootMapping in state.CustomRootMappings) - { - customRootMapping.Insert(0, nameof(TransparentIdentifier.Outer)); - } + foreach (var mapping in state.SourceMappings) + { + var nodes = include + ? mapping.NavigationTree.Flatten().Where(n => (n.Included == NavigationTreeNodeIncludeMode.ReferenceComplete + || n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete + || n.Navigation.ForeignKey.IsOwnership) + && n != navigationTree) + : mapping.NavigationTree.Flatten().Where(n => (n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete + || n.Navigation.ForeignKey.IsOwnership) + && n != navigationTree); + + foreach (var navigationTreeNode in nodes) + { + navigationTreeNode.ToMapping.Insert(0, nameof(TransparentIdentifier.Outer)); + } + } - if (include) - { - navigationTree.Included = NavigationTreeNodeIncludeMode.ReferenceComplete; - } - else - { - navigationTree.ExpansionMode = NavigationTreeNodeExpansionMode.ReferenceComplete; + // TODO: there shouldn't be any custom root mappings for inner, but not 100% sure - think & TEST! + foreach (var customRootMapping in state.CustomRootMappings) + { + customRootMapping.Insert(0, nameof(TransparentIdentifier.Outer)); + } + + if (include) + { + navigationTree.Included = NavigationTreeNodeIncludeMode.ReferenceComplete; + } + else + { + navigationTree.ExpansionMode = NavigationTreeNodeExpansionMode.ReferenceComplete; + } + // finally, we need to incorporate the newly created navigation tree into the old one + navigationRootSourceMapping.NavigationTree.SetNavigation(navigationTree.Navigation); + navigationTree.Parent.AddChild(navigationRootSourceMapping.NavigationTree); + navigationPath.Add(navigation); } - navigationPath.Add(navigation); } else { @@ -223,12 +304,158 @@ var transparentIdentifierCtorInfo child, state, navigationPath.ToList(), - include); + include, + navigationExpandingVisitor, + queryCompilationContext); } return result; } + private static void LegacyCodepath( + ref Expression sourceExpression, + ref ParameterExpression parameterExpression, + NavigationTreeNode navigationTree, + NavigationExpansionExpressionState state, + List navigationPath, + bool include) + { + var navigation = navigationTree.Navigation; + var sourceType = sourceExpression.Type.GetSequenceType(); + var navigationTargetEntityType = navigation.GetTargetType(); + + var entityQueryable = NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(navigationTargetEntityType.ClrType); + var resultType = typeof(TransparentIdentifier<,>).MakeGenericType(sourceType, navigationTargetEntityType.ClrType); + + var outerParameter = Expression.Parameter(sourceType, parameterExpression.Name); + var outerKeySelectorParameter = outerParameter; + var transparentIdentifierAccessorExpression = outerParameter.BuildPropertyAccess(navigationTree.Parent.ToMapping); + + var outerKeySelectorBody = CreateKeyAccessExpression( + transparentIdentifierAccessorExpression, + navigation.IsDependentToPrincipal() + ? navigation.ForeignKey.Properties + : navigation.ForeignKey.PrincipalKey.Properties, + addNullCheck: navigationTree.Parent != null && navigationTree.Parent.Optional); + + var innerKeySelectorParameterType = navigationTargetEntityType.ClrType; + var innerKeySelectorParameter = Expression.Parameter( + innerKeySelectorParameterType, + parameterExpression.Name + "." + navigationTree.Navigation.Name); + + var innerKeySelectorBody = CreateKeyAccessExpression( + innerKeySelectorParameter, + navigation.IsDependentToPrincipal() + ? navigation.ForeignKey.PrincipalKey.Properties + : navigation.ForeignKey.Properties); + + if (outerKeySelectorBody.Type.IsNullableType() + && !innerKeySelectorBody.Type.IsNullableType()) + { + innerKeySelectorBody = Expression.Convert(innerKeySelectorBody, outerKeySelectorBody.Type); + } + else if (innerKeySelectorBody.Type.IsNullableType() + && !outerKeySelectorBody.Type.IsNullableType()) + { + outerKeySelectorBody = Expression.Convert(outerKeySelectorBody, innerKeySelectorBody.Type); + } + + var outerKeySelector = Expression.Lambda( + outerKeySelectorBody, + outerKeySelectorParameter); + + var innerKeySelector = Expression.Lambda( + innerKeySelectorBody, + innerKeySelectorParameter); + + if (!sourceExpression.Type.IsQueryableType()) + { + var asQueryableMethodInfo = LinqMethodHelpers.AsQueryable.MakeGenericMethod(sourceType); + sourceExpression = Expression.Call(asQueryableMethodInfo, sourceExpression); + } + + var joinMethodInfo = navigationTree.Optional + ? _leftJoinMethodInfo.MakeGenericMethod( + sourceType, + navigationTargetEntityType.ClrType, + outerKeySelector.Body.Type, + resultType) + : LinqMethodHelpers.QueryableJoinMethodInfo.MakeGenericMethod( + sourceType, + navigationTargetEntityType.ClrType, + outerKeySelector.Body.Type, + resultType); + + var resultSelectorOuterParameterName = outerParameter.Name; + var resultSelectorOuterParameter = Expression.Parameter(sourceType, resultSelectorOuterParameterName); + + var resultSelectorInnerParameterName = innerKeySelectorParameter.Name; + var resultSelectorInnerParameter = Expression.Parameter(navigationTargetEntityType.ClrType, resultSelectorInnerParameterName); + + var transparentIdentifierCtorInfo + = resultType.GetTypeInfo().GetConstructors().Single(); + + var transparentIdentifierOuterMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Outer"); + var transparentIdentifierInnerMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Inner"); + + var resultSelector = Expression.Lambda( + Expression.New( + transparentIdentifierCtorInfo, + new[] { resultSelectorOuterParameter, resultSelectorInnerParameter }, + new[] { transparentIdentifierOuterMemberInfo, transparentIdentifierInnerMemberInfo }), + resultSelectorOuterParameter, + resultSelectorInnerParameter); + + var joinMethodCall = Expression.Call( + joinMethodInfo, + sourceExpression, + entityQueryable, + outerKeySelector, + innerKeySelector, + resultSelector); + + sourceExpression = joinMethodCall; + + var transparentIdentifierParameterName = resultSelectorInnerParameterName; + var transparentIdentifierParameter = Expression.Parameter(resultSelector.ReturnType, transparentIdentifierParameterName); + parameterExpression = transparentIdentifierParameter; + + // remap navigation 'To' paths -> for this navigation prepend "Inner", for every other (already expanded) navigation prepend "Outer" + navigationTree.ToMapping.Insert(0, nameof(TransparentIdentifier.Inner)); + foreach (var mapping in state.SourceMappings) + { + var nodes = include + ? mapping.NavigationTree.Flatten().Where(n => (n.Included == NavigationTreeNodeIncludeMode.ReferenceComplete + || n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete + || n.Navigation.ForeignKey.IsOwnership) + && n != navigationTree) + : mapping.NavigationTree.Flatten().Where(n => (n.ExpansionMode == NavigationTreeNodeExpansionMode.ReferenceComplete + || n.Navigation.ForeignKey.IsOwnership) + && n != navigationTree); + + foreach (var navigationTreeNode in nodes) + { + navigationTreeNode.ToMapping.Insert(0, nameof(TransparentIdentifier.Outer)); + } + } + + foreach (var customRootMapping in state.CustomRootMappings) + { + customRootMapping.Insert(0, nameof(TransparentIdentifier.Outer)); + } + + if (include) + { + navigationTree.Included = NavigationTreeNodeIncludeMode.ReferenceComplete; + } + else + { + navigationTree.ExpansionMode = NavigationTreeNodeExpansionMode.ReferenceComplete; + + } + navigationPath.Add(navigation); + } + public static Expression CreateKeyAccessExpression( Expression target, IReadOnlyList properties, bool addNullCheck = false) => properties.Count == 1 @@ -290,5 +517,34 @@ public static TResult MaterializeCollectionNavigation( return (TResult)collection; } + + public static Expression RemoveMaterializeCollection(Expression expression) + { + if (expression is NavigationExpansionExpression navigationExpansionExpression + && navigationExpansionExpression.State.MaterializeCollectionNavigation != null) + { + navigationExpansionExpression.State.MaterializeCollectionNavigation = null; + + return new NavigationExpansionExpression( + navigationExpansionExpression.Operand, + navigationExpansionExpression.State, + navigationExpansionExpression.Operand.Type); + } + + if (expression is NavigationExpansionRootExpression navigationExpansionRootExpression + && navigationExpansionRootExpression.NavigationExpansion.State.MaterializeCollectionNavigation != null) + { + navigationExpansionRootExpression.NavigationExpansion.State.MaterializeCollectionNavigation = null; + + var rewritten = new NavigationExpansionExpression( + navigationExpansionRootExpression.NavigationExpansion.Operand, + navigationExpansionRootExpression.NavigationExpansion.State, + navigationExpansionRootExpression.NavigationExpansion.Operand.Type); + + return new NavigationExpansionRootExpression(rewritten, navigationExpansionRootExpression.Mapping); + } + + return expression; + } } } diff --git a/src/EFCore/Query/NavigationExpansion/NavigationTreeNode.cs b/src/EFCore/Query/NavigationExpansion/NavigationTreeNode.cs index e1e266d7d3b..b6d74806314 100644 --- a/src/EFCore/Query/NavigationExpansion/NavigationTreeNode.cs +++ b/src/EFCore/Query/NavigationExpansion/NavigationTreeNode.cs @@ -132,6 +132,56 @@ public static NavigationTreeNode Create( return result; } + private static void PrependFromMappings(NavigationTreeNode navigationTreeNode, List> fromMappingsToPrepend) + { + var newFromMappings = new List>(); + foreach (var parentFromMapping in fromMappingsToPrepend) + { + foreach (var fromMapping in navigationTreeNode.FromMappings) + { + var newMapping = parentFromMapping.ToList(); + newMapping.AddRange(fromMapping); + newFromMappings.Add(newMapping); + } + } + + navigationTreeNode.FromMappings = newFromMappings; + foreach (var child in navigationTreeNode.Children) + { + PrependFromMappings(child, fromMappingsToPrepend); + } + } + + public void AddChild([NotNull] NavigationTreeNode childNode, bool propagateFromMappings = true) + { + Check.NotNull(childNode, nameof(childNode)); + + // when adding the first child - propagate FromMappings from the parent + if (propagateFromMappings) + { + PrependFromMappings(childNode, FromMappings); + } + + var existingChild = Children.Where(c => c.Navigation == childNode.Navigation).SingleOrDefault(); + if (existingChild != null) + { + // if the child exisits, copy ToMappings, add new unique FromMappings and try adding it's children + // however for those children we don't need to re-propagate the mappings, since they are already in place + var newMappings = childNode.FromMappings.Where(m => !existingChild.FromMappings.Any(em => em.SequenceEqual(m))); + existingChild.ToMapping = childNode.ToMapping; + existingChild.FromMappings.AddRange(newMappings); + foreach (var grandChild in existingChild.Children) + { + existingChild.AddChild(grandChild, propagateFromMappings: false); + } + } + else + { + Children.Add(childNode); + childNode.Parent = this; + } + } + public List Flatten() { var result = new List(); @@ -150,5 +200,12 @@ public void MakeOptional() { Optional = true; } + + // TODO: hack - refactor this so that it's not needed + internal void SetNavigation(INavigation navigation) + { + Navigation = navigation; + PrependFromMappings(this, new List> { new List { navigation.Name } }); + } } } diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/CollectionNavigationRewritingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/CollectionNavigationRewritingVisitor.cs index 4468642b436..e32f654ba01 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/CollectionNavigationRewritingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/CollectionNavigationRewritingVisitor.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Query.Pipeline; namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors { @@ -19,10 +20,17 @@ namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors public class CollectionNavigationRewritingVisitor : ExpressionVisitor { private readonly ParameterExpression _sourceParameter; + private readonly NavigationExpandingVisitor _navigationExpandingVisitor; + private readonly QueryCompilationContext _queryCompilationContext; - public CollectionNavigationRewritingVisitor(ParameterExpression sourceParameter) + public CollectionNavigationRewritingVisitor( + ParameterExpression sourceParameter, + NavigationExpandingVisitor navigationExpandingVisitor, + QueryCompilationContext queryCompilationContext) { _sourceParameter = sourceParameter; + _navigationExpandingVisitor = navigationExpandingVisitor; + _queryCompilationContext = queryCompilationContext; } protected override Expression VisitLambda(Expression lambdaExpression) @@ -61,7 +69,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp && methodCallExpression.Method.DeclaringType.IsGenericType && methodCallExpression.Method.DeclaringType.GetGenericTypeDefinition() == typeof(List<>)) { - var newCaller = RemoveMaterializeCollection(Visit(methodCallExpression.Object)); + var newCaller = NavigationExpansionHelpers.RemoveMaterializeCollection(Visit(methodCallExpression.Object)); var newPredicate = Visit(methodCallExpression.Arguments[0]); return Expression.Call( @@ -79,7 +87,7 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp && navigationBindingCaller.NavigationTreeNode.Navigation != null && navigationBindingCaller.NavigationTreeNode.Navigation.IsCollection()) { - var newCaller = RemoveMaterializeCollection(Visit(methodCallExpression.Object)); + var newCaller = NavigationExpansionHelpers.RemoveMaterializeCollection(Visit(methodCallExpression.Object)); var newArgument = Visit(methodCallExpression.Arguments[0]); var lambdaParameter = Expression.Parameter(newCaller.Type.GetSequenceType(), newCaller.Type.GetSequenceType().GenerateParameterName()); @@ -93,13 +101,13 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp lambda); } - var newObject = RemoveMaterializeCollection(Visit(methodCallExpression.Object)); + var newObject = NavigationExpansionHelpers.RemoveMaterializeCollection(Visit(methodCallExpression.Object)); var newArguments = new List(); var argumentsChanged = false; foreach (var argument in methodCallExpression.Arguments) { - var newArgument = RemoveMaterializeCollection(Visit(argument)); + var newArgument = NavigationExpansionHelpers.RemoveMaterializeCollection(Visit(argument)); newArguments.Add(newArgument); if (newArgument != argument) { @@ -112,36 +120,12 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp : methodCallExpression; } - private Expression RemoveMaterializeCollection(Expression expression) - { - if (expression is NavigationExpansionExpression navigationExpansionExpression - && navigationExpansionExpression.State.MaterializeCollectionNavigation != null) - { - navigationExpansionExpression.State.MaterializeCollectionNavigation = null; - - return new NavigationExpansionExpression( - navigationExpansionExpression.Operand, - navigationExpansionExpression.State, - navigationExpansionExpression.Operand.Type); - } - - if (expression is NavigationExpansionRootExpression navigationExpansionRootExpression - && navigationExpansionRootExpression.NavigationExpansion.State.MaterializeCollectionNavigation != null) - { - navigationExpansionRootExpression.NavigationExpansion.State.MaterializeCollectionNavigation = null; - - var rewritten = new NavigationExpansionExpression( - navigationExpansionRootExpression.NavigationExpansion.Operand, - navigationExpansionRootExpression.NavigationExpansion.State, - navigationExpansionRootExpression.NavigationExpansion.Operand.Type); - - return new NavigationExpansionRootExpression(rewritten, navigationExpansionRootExpression.Mapping); - } - - return expression; - } - - public static Expression CreateCollectionNavigationExpression(NavigationTreeNode navigationTreeNode, ParameterExpression rootParameter, SourceMapping sourceMapping) + public static NavigationExpansionExpression CreateCollectionNavigationExpression( + NavigationTreeNode navigationTreeNode, + ParameterExpression rootParameter, + SourceMapping sourceMapping, + NavigationExpandingVisitor navigationExpandingVisitor, + QueryCompilationContext queryCompilationContext) { var collectionEntityType = navigationTreeNode.Navigation.ForeignKey.DeclaringEntityType; var entityQueryable = (Expression)NullAsyncQueryProvider.Instance.CreateEntityQueryableExpression(collectionEntityType.ClrType); @@ -176,7 +160,7 @@ public static Expression CreateCollectionNavigationExpression(NavigationTreeNode entityQueryable, predicate); - var result = NavigationExpansionHelpers.CreateNavigationExpansionRoot(operand, collectionEntityType, navigationTreeNode.Navigation); + var result = NavigationExpansionHelpers.CreateNavigationExpansionRoot(operand, collectionEntityType, navigationTreeNode.Navigation, navigationExpandingVisitor, queryCompilationContext); // this is needed for cases like: root.Include(r => r.Collection).ThenInclude(c => c.Reference).Select(r => r.Collection) // result should be elements of the collection navigation with their 'Reference' included @@ -196,8 +180,8 @@ protected override Expression VisitExtension(Expression extensionExpression) && lastNavigation.IsCollection()) { return lastNavigation.ForeignKey.IsOwnership - ? NavigationExpansionHelpers.CreateNavigationExpansionRoot(navigationBindingExpression, lastNavigation.GetTargetType(), lastNavigation) - : CreateCollectionNavigationExpression(navigationBindingExpression.NavigationTreeNode, navigationBindingExpression.RootParameter, navigationBindingExpression.SourceMapping); + ? NavigationExpansionHelpers.CreateNavigationExpansionRoot(navigationBindingExpression, lastNavigation.GetTargetType(), lastNavigation, _navigationExpandingVisitor, _queryCompilationContext) + : CreateCollectionNavigationExpression(navigationBindingExpression.NavigationTreeNode, navigationBindingExpression.RootParameter, navigationBindingExpression.SourceMapping, _navigationExpandingVisitor, _queryCompilationContext); } else { @@ -210,7 +194,7 @@ protected override Expression VisitExtension(Expression extensionExpression) protected override Expression VisitMember(MemberExpression memberExpression) { - var newExpression = RemoveMaterializeCollection(Visit(memberExpression.Expression)); + var newExpression = NavigationExpansionHelpers.RemoveMaterializeCollection(Visit(memberExpression.Expression)); if (newExpression != memberExpression.Expression) { if (memberExpression.Member.Name == nameof(List.Count)) diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/IncludeApplyingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/IncludeApplyingVisitor.cs index 318d123d207..748d93ecee2 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/IncludeApplyingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/IncludeApplyingVisitor.cs @@ -4,11 +4,21 @@ using System.Linq; using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Query.Pipeline; namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors { public class PendingSelectorIncludeRewriter : ExpressionVisitor { + private readonly NavigationExpandingVisitor _navigationExpandingVisitor; + private readonly QueryCompilationContext _queryCompilationContext; + + public PendingSelectorIncludeRewriter(NavigationExpandingVisitor navigationExpandingVisitor, QueryCompilationContext queryCompilationContext) + { + _navigationExpandingVisitor = navigationExpandingVisitor; + _queryCompilationContext = queryCompilationContext; + } + protected override Expression VisitMember(MemberExpression memberExpression) => memberExpression; protected override Expression VisitInvocation(InvocationExpression invocationExpression) => invocationExpression; protected override Expression VisitLambda(Expression lambdaExpression) => lambdaExpression; @@ -88,7 +98,7 @@ private IncludeExpression CreateIncludeReferenceCall(Expression caller, Navigati private IncludeExpression CreateIncludeCollectionCall(Expression caller, NavigationTreeNode node, ParameterExpression rootParameter, SourceMapping sourceMapping) { - var included = CollectionNavigationRewritingVisitor.CreateCollectionNavigationExpression(node, rootParameter, sourceMapping); + var included = CollectionNavigationRewritingVisitor.CreateCollectionNavigationExpression(node, rootParameter, sourceMapping, _navigationExpandingVisitor, _queryCompilationContext); return new IncludeExpression(caller, included, node.Navigation); } diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs index a8f875c68dd..b63f43741e6 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor.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.Generic; using System.Linq; using System.Linq.Expressions; using System.Reflection; @@ -14,13 +15,15 @@ namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors { public partial class NavigationExpandingVisitor : ExpressionVisitor { - private readonly IModel _model; + private readonly QueryCompilationContext _queryCompilationContext; - public NavigationExpandingVisitor(IModel model) + public NavigationExpandingVisitor(QueryCompilationContext queryCompilationContext) { - _model = model; + _queryCompilationContext = queryCompilationContext; } + public virtual List AppliedQueryFilters { get; } = new List(); + protected override Expression VisitExtension(Expression extensionExpression) { if (extensionExpression is NavigationBindingExpression navigationBindingExpression) @@ -48,6 +51,14 @@ protected override Expression VisitExtension(Expression extensionExpression) return navigationExpansionExpression; } + if (extensionExpression is MaterializeCollectionNavigationExpression materializeCollectionNavigationExpression) + { + var newOperand = VisitSourceExpression(materializeCollectionNavigationExpression.Operand); + newOperand.State.MaterializeCollectionNavigation = materializeCollectionNavigationExpression.Navigation; + + return new NavigationExpansionExpression(newOperand.Operand, newOperand.State, materializeCollectionNavigationExpression.Type); + } + return base.VisitExtension(extensionExpression); } @@ -165,7 +176,7 @@ private Expression ProcessMemberPushdown( materializeCollectionNavigation: null); var rewrittenNavigationExpansionExpression = new NavigationExpansionExpression(navigationExpansionExpression.Operand, rewrittenState, combinedKeySelectorBody.Type); - var inner = new NavigationExpansionReducingVisitor().Visit(rewrittenNavigationExpansionExpression); + var inner = new NavigationExpansionReducingVisitor(this, _queryCompilationContext).Visit(rewrittenNavigationExpansionExpression); var predicate = Expression.Lambda( Expression.Equal(outerKeyAccess, inner), @@ -179,7 +190,7 @@ private Expression ProcessMemberPushdown( var entityType = lastNavigation.ForeignKey.DeclaringEntityType; - return NavigationExpansionHelpers.CreateNavigationExpansionRoot(rewritten, entityType, materializeCollectionNavigation: null); + return NavigationExpansionHelpers.CreateNavigationExpansionRoot(rewritten, entityType, materializeCollectionNavigation: null, this, _queryCompilationContext); } else { @@ -246,7 +257,7 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) if (binaryExpression.Left is MemberExpression leftMember && leftMember.Type.TryGetSequenceType() is Type leftSequenceType && leftSequenceType != null - && _model.FindEntityType(leftMember.Expression.Type) is IEntityType leftParentEntityType) + && _queryCompilationContext.Model.FindEntityType(leftMember.Expression.Type) is IEntityType leftParentEntityType) { leftNavigation = leftParentEntityType.FindNavigation(leftMember.Member.Name); if (leftNavigation != null) @@ -258,7 +269,7 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) if (binaryExpression.Right is MemberExpression rightMember && rightMember.Type.TryGetSequenceType() is Type rightSequenceType && rightSequenceType != null - && _model.FindEntityType(rightMember.Expression.Type) is IEntityType rightParentEntityType) + && _queryCompilationContext.Model.FindEntityType(rightMember.Expression.Type) is IEntityType rightParentEntityType) { rightNavigation = rightParentEntityType.FindNavigation(rightMember.Member.Name); if (rightNavigation != null) diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs index bd580abbd18..020e170636c 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpandingVisitor_MethodCall.cs @@ -6,7 +6,6 @@ using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Xml; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; @@ -190,7 +189,7 @@ private Expression ProcessWhere(MethodCallExpression methodCallExpression) AdjustCurrentParameterName(source.State, predicate.Parameters[0].Name); var appliedNavigationsResult = FindAndApplyNavigations(source.Operand, predicate, source.State); - var newPredicateBody = new NavigationPropertyUnbindingVisitor(appliedNavigationsResult.state.CurrentParameter).Visit(appliedNavigationsResult.lambdaBody); + var newPredicateBody = new NavigationPropertyUnbindingVisitor(appliedNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(appliedNavigationsResult.lambdaBody); var newPredicateLambda = Expression.Lambda(newPredicateBody, appliedNavigationsResult.state.CurrentParameter); var appliedOrderingsResult = ApplyPendingOrderings(appliedNavigationsResult.source, appliedNavigationsResult.state); @@ -321,7 +320,7 @@ private Expression ProcessSelectMany(MethodCallExpression methodCallExpression) collectionSelectorLambdaBody, outerState.CurrentParameter); - newCollectionSelectorLambda = (LambdaExpression)new NavigationPropertyUnbindingVisitor(outerState.CurrentParameter).Visit(newCollectionSelectorLambda); + newCollectionSelectorLambda = (LambdaExpression)new NavigationPropertyUnbindingVisitor(outerState.CurrentParameter, this, _queryCompilationContext).Visit(newCollectionSelectorLambda); if (methodCallExpression.Method.MethodIsClosedFormOf(LinqMethodHelpers.QueryableSelectManyMethodInfo)) { @@ -528,8 +527,8 @@ private Expression ProcessJoin(MethodCallExpression methodCallExpression) var outerApplyNavigationsResult = FindAndApplyNavigations(outerSource.Operand, outerKeySelector, outerSource.State); var innerApplyNavigationsResult = FindAndApplyNavigations(innerSource.Operand, innerKeySelector, innerSource.State); - var newOuterKeySelectorBody = new NavigationPropertyUnbindingVisitor(outerApplyNavigationsResult.state.CurrentParameter).Visit(outerApplyNavigationsResult.lambdaBody); - var newInnerKeySelectorBody = new NavigationPropertyUnbindingVisitor(innerApplyNavigationsResult.state.CurrentParameter).Visit(innerApplyNavigationsResult.lambdaBody); + var newOuterKeySelectorBody = new NavigationPropertyUnbindingVisitor(outerApplyNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(outerApplyNavigationsResult.lambdaBody); + var newInnerKeySelectorBody = new NavigationPropertyUnbindingVisitor(innerApplyNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(innerApplyNavigationsResult.lambdaBody); var outerApplyOrderingsResult = ApplyPendingOrderings(outerApplyNavigationsResult.source, outerApplyNavigationsResult.state); var innerApplyOrderingsResult = ApplyPendingOrderings(innerApplyNavigationsResult.source, innerApplyNavigationsResult.state); @@ -577,8 +576,8 @@ private Expression ProcessGroupJoin(MethodCallExpression methodCallExpression) var outerApplyNavigationsResult = FindAndApplyNavigations(outerSource.Operand, outerKeySelector, outerSource.State); var innerApplyNavigationsResult = FindAndApplyNavigations(innerSource.Operand, innerKeySelector, innerSource.State); - var newOuterKeySelectorBody = new NavigationPropertyUnbindingVisitor(outerApplyNavigationsResult.state.CurrentParameter).Visit(outerApplyNavigationsResult.lambdaBody); - var newInnerKeySelectorBody = new NavigationPropertyUnbindingVisitor(innerApplyNavigationsResult.state.CurrentParameter).Visit(innerApplyNavigationsResult.lambdaBody); + var newOuterKeySelectorBody = new NavigationPropertyUnbindingVisitor(outerApplyNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(outerApplyNavigationsResult.lambdaBody); + var newInnerKeySelectorBody = new NavigationPropertyUnbindingVisitor(innerApplyNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(innerApplyNavigationsResult.lambdaBody); var outerApplyOrderingsResult = ApplyPendingOrderings(outerApplyNavigationsResult.source, outerApplyNavigationsResult.state); var innerApplyOrderingsResult = ApplyPendingOrderings(innerApplyNavigationsResult.source, innerApplyNavigationsResult.state); @@ -683,7 +682,7 @@ private Expression ProcessAll(MethodCallExpression methodCallExpression) AdjustCurrentParameterName(source.State, predicate.Parameters[0].Name); var applyNavigationsResult = FindAndApplyNavigations(source.Operand, predicate, source.State); - var newPredicateBody = new NavigationPropertyUnbindingVisitor(applyNavigationsResult.state.CurrentParameter).Visit(applyNavigationsResult.lambdaBody); + var newPredicateBody = new NavigationPropertyUnbindingVisitor(applyNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(applyNavigationsResult.lambdaBody); var applyOrderingsResult = ApplyPendingOrderings(applyNavigationsResult.source, applyNavigationsResult.state); var newMethodInfo = methodCallExpression.Method.GetGenericMethodDefinition().MakeGenericMethod(applyOrderingsResult.state.CurrentParameter.Type); @@ -757,7 +756,7 @@ private Expression ProcessAverageSumMinMax(MethodCallExpression methodCallExpres var selector = methodCallExpression.Arguments[1].UnwrapLambdaFromQuote(); AdjustCurrentParameterName(source.State, selector.Parameters[0].Name); var applyNavigationsResult = FindAndApplyNavigations(source.Operand, selector, source.State); - var newSelectorBody = new NavigationPropertyUnbindingVisitor(applyNavigationsResult.state.CurrentParameter).Visit(applyNavigationsResult.lambdaBody); + var newSelectorBody = new NavigationPropertyUnbindingVisitor(applyNavigationsResult.state.CurrentParameter, this, _queryCompilationContext).Visit(applyNavigationsResult.lambdaBody); var newSelector = Expression.Lambda(newSelectorBody, applyNavigationsResult.state.CurrentParameter); var applyOrderingsResult = ApplyPendingOrderings(applyNavigationsResult.source, applyNavigationsResult.state); @@ -818,7 +817,7 @@ private Expression ProcessOfType(MethodCallExpression methodCallExpression) { var source = VisitSourceExpression(methodCallExpression.Arguments[0]); var preProcessResult = PreProcessTerminatingOperation(source); - var newEntityType = _model.FindEntityType(methodCallExpression.Method.GetGenericArguments()[0]); + var newEntityType = _queryCompilationContext.Model.FindEntityType(methodCallExpression.Method.GetGenericArguments()[0]); // TODO: possible small optimization - only apply this if newEntityType is different than the old one if (newEntityType != null) @@ -859,7 +858,7 @@ private Expression ProcessSkipTake(MethodCallExpression methodCallExpression) if (applyOrderingsResult.state.ApplyPendingSelector) { - var unbinder = new NavigationPropertyUnbindingVisitor(applyOrderingsResult.state.CurrentParameter); + var unbinder = new NavigationPropertyUnbindingVisitor(applyOrderingsResult.state.CurrentParameter, this, _queryCompilationContext); var newSelectorBody = unbinder.Visit(applyOrderingsResult.state.PendingSelector.Body); var pssmg = new PendingSelectorSourceMappingGenerator(applyOrderingsResult.state.PendingSelector.Parameters[0], null); @@ -1268,9 +1267,9 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio && constantExpression.Value.GetType().GetGenericTypeDefinition() == typeof(EntityQueryable<>)) { var elementType = constantExpression.Value.GetType().GetSequenceType(); - var entityType = _model.FindEntityType(elementType); + var entityType = _queryCompilationContext.Model.FindEntityType(elementType); - return NavigationExpansionHelpers.CreateNavigationExpansionRoot(constantExpression, entityType, materializeCollectionNavigation: null); + return NavigationExpansionHelpers.CreateNavigationExpansionRoot(constantExpression, entityType, materializeCollectionNavigation: null, this, _queryCompilationContext); } return base.VisitConstant(constantExpression); @@ -1281,7 +1280,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio foreach (var pendingOrdering in state.PendingOrderings) { var remappedKeySelectorBody = new ExpressionReplacingVisitor(pendingOrdering.keySelector.Parameters[0], state.CurrentParameter).Visit(pendingOrdering.keySelector.Body); - var newSelectorBody = new NavigationPropertyUnbindingVisitor(state.CurrentParameter).Visit(remappedKeySelectorBody); + var newSelectorBody = new NavigationPropertyUnbindingVisitor(state.CurrentParameter, this, _queryCompilationContext).Visit(remappedKeySelectorBody); var newSelector = Expression.Lambda(newSelectorBody, state.CurrentParameter); var orderingMethod = pendingOrdering.method.MakeGenericMethod(state.CurrentParameter.Type, newSelectorBody.Type); source = Expression.Call(orderingMethod, source, newSelector); @@ -1311,7 +1310,7 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio var boundLambdaBody = binder.Visit(remappedLambdaBody); boundLambdaBody = new NavigationComparisonOptimizingVisitor().Visit(boundLambdaBody); - boundLambdaBody = new CollectionNavigationRewritingVisitor(state.CurrentParameter).Visit(boundLambdaBody); + boundLambdaBody = new CollectionNavigationRewritingVisitor(state.CurrentParameter, this, _queryCompilationContext).Visit(boundLambdaBody); boundLambdaBody = Visit(boundLambdaBody); var result = (source, parameter: state.CurrentParameter); @@ -1330,7 +1329,9 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio navigationTree, state, new List(), - include: false); + include: false, + this, + _queryCompilationContext); } applyPendingSelector = true; diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs index c0f601d449d..611da01aafd 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationExpansionReducingVisitor.cs @@ -9,11 +9,21 @@ using System.Reflection; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Query.Pipeline; namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors { public class NavigationExpansionReducingVisitor : ExpressionVisitor { + private readonly NavigationExpandingVisitor _navigationExpandingVisitor; + private readonly QueryCompilationContext _queryCompilationContext; + + public NavigationExpansionReducingVisitor(NavigationExpandingVisitor navigationExpandingVisitor, QueryCompilationContext queryCompilationContext) + { + _navigationExpandingVisitor = navigationExpandingVisitor; + _queryCompilationContext = queryCompilationContext; + } + protected override Expression VisitExtension(Expression extensionExpression) { if (extensionExpression is NavigationBindingExpression navigationBindingExpression) @@ -48,7 +58,7 @@ protected override Expression VisitExtension(Expression extensionExpression) foreach (var pendingOrdering in state.PendingOrderings) { var remappedKeySelectorBody = new ExpressionReplacingVisitor(pendingOrdering.keySelector.Parameters[0], state.CurrentParameter).Visit(pendingOrdering.keySelector.Body); - var newSelectorBody = new NavigationPropertyUnbindingVisitor(state.CurrentParameter).Visit(remappedKeySelectorBody); + var newSelectorBody = new NavigationPropertyUnbindingVisitor(state.CurrentParameter, _navigationExpandingVisitor, _queryCompilationContext).Visit(remappedKeySelectorBody); var newSelector = Expression.Lambda(newSelectorBody, state.CurrentParameter); var orderingMethod = pendingOrdering.method.MakeGenericMethod(state.CurrentParameter.Type, newSelectorBody.Type); result = Expression.Call(orderingMethod, result, newSelector); @@ -56,7 +66,7 @@ protected override Expression VisitExtension(Expression extensionExpression) if (state.ApplyPendingSelector) { - var pendingSelector = (LambdaExpression)new NavigationPropertyUnbindingVisitor(state.CurrentParameter).Visit(state.PendingSelector); + var pendingSelector = (LambdaExpression)new NavigationPropertyUnbindingVisitor(state.CurrentParameter, _navigationExpandingVisitor, _queryCompilationContext).Visit(state.PendingSelector); var pendingSelectorBodyType = pendingSelector.Type.GetGenericArguments()[1]; var pendingSelectMathod = result.Type.IsGenericType && (result.Type.GetGenericTypeDefinition() == typeof(IEnumerable<>) || result.Type.GetGenericTypeDefinition() == typeof(IOrderedEnumerable<>)) @@ -122,7 +132,7 @@ protected override Expression VisitExtension(Expression extensionExpression) var includeFinder = new PendingIncludeFindingVisitor(); includeFinder.Visit(navigationExpansionExpression.State.PendingSelector.Body); - var includeRewriter = new PendingSelectorIncludeRewriter(); + var includeRewriter = new PendingSelectorIncludeRewriter(_navigationExpandingVisitor, _queryCompilationContext); var rewrittenBody = includeRewriter.Visit(navigationExpansionExpression.State.PendingSelector.Body); if (navigationExpansionExpression.State.PendingSelector.Body != rewrittenBody) @@ -143,7 +153,9 @@ protected override Expression VisitExtension(Expression extensionExpression) pendingIncludeNode.Key, navigationExpansionExpression.State, new List(), - include: true); + include: true, + _navigationExpandingVisitor, + _queryCompilationContext); } var pendingSelector = navigationExpansionExpression.State.PendingSelector; diff --git a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationPropertyUnbindingVisitor.cs b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationPropertyUnbindingVisitor.cs index 07d37d93a3d..5293ae8e1bf 100644 --- a/src/EFCore/Query/NavigationExpansion/Visitors/NavigationPropertyUnbindingVisitor.cs +++ b/src/EFCore/Query/NavigationExpansion/Visitors/NavigationPropertyUnbindingVisitor.cs @@ -3,16 +3,24 @@ using System.Linq.Expressions; using Microsoft.EntityFrameworkCore.Internal; +using Microsoft.EntityFrameworkCore.Query.Pipeline; namespace Microsoft.EntityFrameworkCore.Query.NavigationExpansion.Visitors { public class NavigationPropertyUnbindingVisitor : ExpressionVisitor { private readonly ParameterExpression _rootParameter; + private readonly NavigationExpandingVisitor _navigationExpandingVisitor; + private readonly QueryCompilationContext _queryCompilationContext; - public NavigationPropertyUnbindingVisitor(ParameterExpression rootParameter) + public NavigationPropertyUnbindingVisitor( + ParameterExpression rootParameter, + NavigationExpandingVisitor navigationExpandingVisitor, + QueryCompilationContext queryCompilationContext) { _rootParameter = rootParameter; + _navigationExpandingVisitor = navigationExpandingVisitor; + _queryCompilationContext = queryCompilationContext; } protected override Expression VisitExtension(Expression extensionExpression) @@ -40,7 +48,7 @@ protected override Expression VisitExtension(Expression extensionExpression) if (extensionExpression is NavigationExpansionRootExpression || extensionExpression is NavigationExpansionExpression) { - var result = new NavigationExpansionReducingVisitor().Visit(extensionExpression); + var result = new NavigationExpansionReducingVisitor(_navigationExpandingVisitor, _queryCompilationContext).Visit(extensionExpression); return Visit(result); } diff --git a/src/EFCore/Query/Pipeline/QueryCompilationContext.cs b/src/EFCore/Query/Pipeline/QueryCompilationContext.cs index 067076c4481..11b7b1ff763 100644 --- a/src/EFCore/Query/Pipeline/QueryCompilationContext.cs +++ b/src/EFCore/Query/Pipeline/QueryCompilationContext.cs @@ -2,7 +2,11 @@ // 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; using System.Linq.Expressions; +using System.Reflection; +using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; @@ -19,6 +23,8 @@ public class QueryCompilationContext private readonly IShapedQueryOptimizerFactory _shapedQueryOptimizerFactory; private readonly IShapedQueryCompilingExpressionVisitorFactory _shapedQueryCompilingExpressionVisitorFactory; + private readonly Parameters _parameters; + public QueryCompilationContext( IModel model, IQueryOptimizerFactory queryOptimizerFactory, @@ -28,6 +34,7 @@ public QueryCompilationContext( ICurrentDbContext currentDbContext, IDbContextOptions contextOptions, IDiagnosticsLogger logger, + IEvaluatableExpressionFilter evaluatableExpressionFilter, bool async) { Async = async; @@ -36,21 +43,28 @@ public QueryCompilationContext( ContextOptions = contextOptions; ContextType = currentDbContext.Context.GetType(); Logger = logger; + EvaluatableExpressionFilter = evaluatableExpressionFilter; _queryOptimizerFactory = queryOptimizerFactory; _queryableMethodTranslatingExpressionVisitorFactory = queryableMethodTranslatingExpressionVisitorFactory; _shapedQueryOptimizerFactory = shapedQueryOptimizerFactory; _shapedQueryCompilingExpressionVisitorFactory = shapedQueryCompilingExpressionVisitorFactory; + _parameters = new Parameters(); } public bool Async { get; } public IModel Model { get; } public IDbContextOptions ContextOptions { get; } public bool TrackQueryResults { get; internal set; } + public bool IgnoreQueryFilters { get; internal set; } public virtual IDiagnosticsLogger Logger { get; } public virtual Type ContextType { get; } + public virtual IEvaluatableExpressionFilter EvaluatableExpressionFilter { get; set; } + + internal virtual IParameterValues ParameterValues => _parameters; + public virtual Func CreateQueryExecutor(Expression query) { query = _queryOptimizerFactory.Create(this).Visit(query); @@ -62,6 +76,16 @@ public virtual Func CreateQueryExecutor(Expressi // Inject tracking query = _shapedQueryCompilingExpressionVisitorFactory.Create(this).Visit(query); + var setFilterParameterExpressions + = CreateSetFilterParametersExpressions(out var contextVariableExpression); + + if (setFilterParameterExpressions != null) + { + query = Expression.Block( + new[] { contextVariableExpression }, + setFilterParameterExpressions.Concat(new[] { query })); + } + var queryExecutorExpression = Expression.Lambda>( query, QueryContextParameter); @@ -75,5 +99,80 @@ public virtual Func CreateQueryExecutor(Expressi Logger.QueryExecutionPlanned(new ExpressionPrinter(), queryExecutorExpression); } } + + private static readonly MethodInfo _queryContextAddParameterMethodInfo + = typeof(QueryContext) + .GetTypeInfo() + .GetDeclaredMethod(nameof(QueryContext.AddParameter)); + + private static readonly PropertyInfo _queryContextContextPropertyInfo + = typeof(QueryContext) + .GetTypeInfo() + .GetDeclaredProperty(nameof(QueryContext.Context)); + + private IEnumerable CreateSetFilterParametersExpressions(out ParameterExpression contextVariableExpression) + { + contextVariableExpression = null; + + if (_parameters.ParameterValues.Count == 0) + { + return null; + } + + contextVariableExpression = Expression.Variable(ContextType, "context"); + + var blockExpressions + = new List + { + Expression.Assign( + contextVariableExpression, + Expression.Convert( + Expression.Property( + QueryContextParameter, + _queryContextContextPropertyInfo), + ContextType)) + }; + + foreach (var keyValuePair in _parameters.ParameterValues) + { + blockExpressions.Add( + Expression.Call( + QueryContextParameter, + _queryContextAddParameterMethodInfo, + Expression.Constant(keyValuePair.Key), + Expression.Convert( + Expression.Invoke( + (LambdaExpression)keyValuePair.Value, + contextVariableExpression), + typeof(object)))); + } + + return blockExpressions; + } + + private class Parameters : IParameterValues + { + private readonly IDictionary _parameterValues = new Dictionary(); + + public IReadOnlyDictionary ParameterValues => (IReadOnlyDictionary)_parameterValues; + + public virtual void AddParameter(string name, object value) + { + _parameterValues.Add(name, value); + } + + public virtual void SetParameter(string name, object value) + { + _parameterValues[name] = value; + } + + public virtual object RemoveParameter(string name) + { + var value = _parameterValues[name]; + _parameterValues.Remove(name); + + return value; + } + } } } diff --git a/src/EFCore/Query/Pipeline/QueryCompilationContextFactory.cs b/src/EFCore/Query/Pipeline/QueryCompilationContextFactory.cs index a4d26f44b55..04b4408e88f 100644 --- a/src/EFCore/Query/Pipeline/QueryCompilationContextFactory.cs +++ b/src/EFCore/Query/Pipeline/QueryCompilationContextFactory.cs @@ -4,6 +4,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Query.Internal; namespace Microsoft.EntityFrameworkCore.Query.Pipeline { @@ -17,6 +18,7 @@ public class QueryCompilationContextFactory : IQueryCompilationContextFactory private readonly ICurrentDbContext _currentDbContext; private readonly IDbContextOptions _contextOptions; private readonly IDiagnosticsLogger _logger; + private readonly IEvaluatableExpressionFilter _evaluatableExpressionFilter; public QueryCompilationContextFactory( IModel model, @@ -26,7 +28,8 @@ public QueryCompilationContextFactory( IShapedQueryCompilingExpressionVisitorFactory shapedQueryCompilingExpressionVisitorFactory, ICurrentDbContext currentDbContext, IDbContextOptions contextOptions, - IDiagnosticsLogger logger) + IDiagnosticsLogger logger, + IEvaluatableExpressionFilter evaluatableExpressionFilter) { _model = model; _queryOptimizerFactory = queryOptimizerFactory; @@ -36,6 +39,7 @@ public QueryCompilationContextFactory( _currentDbContext = currentDbContext; _contextOptions = contextOptions; _logger = logger; + _evaluatableExpressionFilter = evaluatableExpressionFilter; } public QueryCompilationContext Create(bool async) @@ -49,6 +53,7 @@ public QueryCompilationContext Create(bool async) _currentDbContext, _contextOptions, _logger, + _evaluatableExpressionFilter, async); return queryCompilationContext; diff --git a/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs b/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs index 56210bc880c..adeb92cfe50 100644 --- a/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/QueryMetadataExtractingExpressionVisitor.cs @@ -30,6 +30,15 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return innerQueryable; } + + if (genericMethodDefinition == EntityFrameworkQueryableExtensions.IgnoreQueryFiltersMethodInfo) + { + var innerQueryable = Visit(methodCallExpression.Arguments[0]); + + _queryCompilationContext.IgnoreQueryFilters = true; + + return innerQueryable; + } } return base.VisitMethodCall(methodCallExpression); diff --git a/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs b/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs index df95a96b8fa..f2c171e459b 100644 --- a/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs +++ b/src/EFCore/Query/Pipeline/QueryOptimizingExpressionVisitor.cs @@ -19,13 +19,13 @@ public QueryOptimizer(QueryCompilationContext queryCompilationContext) public Expression Visit(Expression query) { + query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query); query = new AllAnyToContainsRewritingExpressionVisitor().Visit(query); query = new GroupJoinFlatteningExpressionVisitor().Visit(query); query = new NullCheckRemovingExpressionVisitor().Visit(query); query = new EntityEqualityRewritingExpressionVisitor(_queryCompilationContext).Rewrite(query); - query = new NavigationExpander(_queryCompilationContext.Model).ExpandNavigations(query); + query = new NavigationExpander(_queryCompilationContext).ExpandNavigations(query); query = new EnumerableToQueryableReMappingExpressionVisitor().Visit(query); - query = new QueryMetadataExtractingExpressionVisitor(_queryCompilationContext).Visit(query); query = new NullCheckRemovingExpressionVisitor().Visit(query); query = new FunctionPreprocessingVisitor().Visit(query); new EnumerableVerifyingExpressionVisitor().Visit(query); diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs index e6c4919e0bc..c4cb462bd6c 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -19,10 +19,10 @@ public class InMemoryComplianceTest : ComplianceTestBase typeof(GraphUpdatesTestBase<>), // issue #15318 typeof(ProxyGraphUpdatesTestBase<>), // issue #15318 typeof(ComplexNavigationsWeakQueryTestBase<>), // issue #15285 - typeof(FiltersInheritanceTestBase<>), // issue #15264 - typeof(FiltersTestBase<>), // issue #15264 typeof(OwnedQueryTestBase<>), // issue #15285 // Query pipeline + typeof(FiltersInheritanceTestBase<>), + typeof(FiltersTestBase<>), typeof(SimpleQueryTestBase<>), typeof(ConcurrencyDetectorTestBase<>), typeof(AsNoTrackingTestBase<>), diff --git a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs index 2fed8bd7aec..07e845f8ddd 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs @@ -5,7 +5,6 @@ namespace Microsoft.EntityFrameworkCore.Query { - //issue #15264 internal class FiltersInMemoryTest : FiltersTestBase> { public FiltersInMemoryTest(NorthwindQueryInMemoryFixture fixture, ITestOutputHelper testOutputHelper) diff --git a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs index 12dd0ad5cb7..5a3ae0a1c50 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs @@ -3,7 +3,6 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 internal class FiltersInheritanceInMemoryTest : FiltersInheritanceTestBase { public FiltersInheritanceInMemoryTest(FiltersInheritanceInMemoryFixture fixture) diff --git a/test/EFCore.Specification.Tests/Query/FiltersInheritanceTestBase.cs b/test/EFCore.Specification.Tests/Query/FiltersInheritanceTestBase.cs index f067175b188..058de68dc43 100644 --- a/test/EFCore.Specification.Tests/Query/FiltersInheritanceTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/FiltersInheritanceTestBase.cs @@ -18,7 +18,7 @@ public abstract class FiltersInheritanceTestBase : IClassFixture("ALFKI")); } - // also issue #15264 [ConditionalFact(Skip = "Issue #14935. Cannot eval 'where ClientMethod([p])'")] public virtual void Client_eval() { Assert.Equal(69, _context.Products.ToList().Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual async Task Materialized_query_async() { Assert.Equal(7, (await _context.Customers.ToListAsync()).Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Materialized_query_parameter() { _context.TenantPrefix = "F"; @@ -68,7 +66,7 @@ public virtual void Materialized_query_parameter() Assert.Equal(8, _context.Customers.ToList().Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Materialized_query_parameter_new_context() { Assert.Equal(7, _context.Customers.ToList().Count); @@ -81,13 +79,13 @@ public virtual void Materialized_query_parameter_new_context() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Projection_query() { Assert.Equal(7, _context.Customers.Select(c => c.CustomerID).ToList().Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Projection_query_parameter() { _context.TenantPrefix = "F"; @@ -95,7 +93,7 @@ public virtual void Projection_query_parameter() Assert.Equal(8, _context.Customers.Select(c => c.CustomerID).ToList().Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Include_query() { var results = _context.Customers.Include(c => c.Orders).ToList(); @@ -103,7 +101,7 @@ public virtual void Include_query() Assert.Equal(7, results.Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Include_query_opt_out() { var results = _context.Customers.Include(c => c.Orders).IgnoreQueryFilters().ToList(); @@ -111,16 +109,24 @@ public virtual void Include_query_opt_out() Assert.Equal(91, results.Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Included_many_to_one_query() { var results = _context.Orders.Include(o => o.Customer).ToList(); - Assert.Equal(830, results.Count); + Assert.Equal(80, results.Count); + Assert.True(results.All(o => o.Customer == null || o.CustomerID.StartsWith("B"))); + } + + [ConditionalFact] + public virtual void Project_reference_that_itself_has_query_filter_with_another_reference() + { + var results = _context.OrderDetails.Select(od => od.Order).ToList(); + + Assert.Equal(5, results.Count); Assert.True(results.All(o => o.Customer == null || o.CustomerID.StartsWith("B"))); } - // also issue #15264 [ConditionalFact(Skip = "Issue #14935. Cannot eval 'where ClientMethod([p])'")] public virtual void Included_one_to_many_query_with_client_eval() { @@ -133,7 +139,7 @@ public virtual void Included_one_to_many_query_with_client_eval() || p.OrderDetails.All(od => od.Quantity > 50))); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact(Skip = "issue #16293")] public virtual void Navs_query() { var results @@ -146,7 +152,7 @@ where od.Discount < 10 Assert.Equal(5, results.Count); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact(Skip = "issue #14551")] public virtual void Compiled_query() { var query = EF.CompileQuery( diff --git a/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs b/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs index 7e6501552e5..d5376a21f1b 100644 --- a/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs @@ -21,7 +21,7 @@ public abstract class QueryFilterFuncletizationTestBase : IClassFixtur protected QueryFilterFuncletizationContext CreateContext() => Fixture.CreateContext(); - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_parameter_does_not_clash_with_closure_parameter_name() { using (var context = CreateContext()) @@ -31,7 +31,7 @@ public virtual void DbContext_property_parameter_does_not_clash_with_closure_par } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -46,7 +46,7 @@ public virtual void DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_is_parameterized() { using (var context = CreateContext()) @@ -61,7 +61,7 @@ public virtual void DbContext_property_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_method_call_is_parameterized() { using (var context = CreateContext()) @@ -71,14 +71,14 @@ public virtual void DbContext_method_call_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_list_is_parameterized() { using (var context = CreateContext()) { // This throws because the default value of TenantIds is null which is NRE var exception = Record.Exception(() => context.Set().ToList()); - Assert.True(exception is InvalidOperationException || exception is ArgumentNullException); + Assert.True(exception is InvalidOperationException || exception is ArgumentNullException || exception is NullReferenceException); context.TenantIds = new List(); var query = context.Set().ToList(); @@ -101,7 +101,7 @@ public virtual void DbContext_list_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_chain_is_parameterized() { using (var context = CreateContext()) @@ -125,7 +125,7 @@ public virtual void DbContext_property_chain_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_method_call_is_parameterized() { using (var context = CreateContext()) @@ -139,7 +139,7 @@ public virtual void DbContext_property_method_call_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_method_call_chain_is_parameterized() { using (var context = CreateContext()) @@ -149,7 +149,7 @@ public virtual void DbContext_method_call_chain_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_complex_expression_is_parameterized() { using (var context = CreateContext()) @@ -167,7 +167,7 @@ public virtual void DbContext_complex_expression_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_based_filter_does_not_short_circuit() { using (var context = CreateContext()) @@ -184,7 +184,7 @@ public virtual void DbContext_property_based_filter_does_not_short_circuit() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -199,7 +199,7 @@ public virtual void EntityTypeConfiguration_DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_property_is_parameterized() { using (var context = CreateContext()) @@ -214,7 +214,7 @@ public virtual void EntityTypeConfiguration_DbContext_property_is_parameterized( } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_method_call_is_parameterized() { using (var context = CreateContext()) @@ -224,7 +224,7 @@ public virtual void EntityTypeConfiguration_DbContext_method_call_is_parameteriz } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_property_chain_is_parameterized() { using (var context = CreateContext()) @@ -248,7 +248,7 @@ public virtual void EntityTypeConfiguration_DbContext_property_chain_is_paramete } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_method_DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -263,7 +263,7 @@ public virtual void Local_method_DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_static_method_DbContext_property_is_parameterized() { using (var context = CreateContext()) @@ -278,7 +278,7 @@ public virtual void Local_static_method_DbContext_property_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Remote_method_DbContext_property_method_call_is_parameterized() { using (var context = CreateContext()) @@ -292,7 +292,7 @@ public virtual void Remote_method_DbContext_property_method_call_is_parameterize } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Extension_method_DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -307,7 +307,7 @@ public virtual void Extension_method_DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Extension_method_DbContext_property_chain_is_parameterized() { using (var context = CreateContext()) @@ -331,7 +331,7 @@ public virtual void Extension_method_DbContext_property_chain_is_parameterized() } } - [ConditionalFact(Skip = "See issue#13587")] + [ConditionalFact(Skip = "See issue#16319")] public virtual void Using_DbSet_in_filter_works() { using (var context = CreateContext()) @@ -340,7 +340,7 @@ public virtual void Using_DbSet_in_filter_works() } } - [ConditionalFact(Skip = "See issue#13587")] + [ConditionalFact(Skip = "See issue#16319")] public virtual void Using_Context_set_method_in_filter_works() { using (var context = CreateContext()) @@ -351,7 +351,7 @@ public virtual void Using_Context_set_method_in_filter_works() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Static_member_from_dbContext_is_inlined() { using (var context = CreateContext()) @@ -362,7 +362,7 @@ public virtual void Static_member_from_dbContext_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Static_member_from_non_dbContext_is_inlined() { using (var context = CreateContext()) @@ -373,7 +373,7 @@ public virtual void Static_member_from_non_dbContext_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_variable_from_OnModelCreating_is_inlined() { using (var context = CreateContext()) @@ -384,7 +384,7 @@ public virtual void Local_variable_from_OnModelCreating_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_variable_from_OnModelCreating_can_throw_exception() { using (var context = CreateContext()) @@ -397,7 +397,7 @@ public virtual void Local_variable_from_OnModelCreating_can_throw_exception() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Method_parameter_is_inlined() { using (var context = CreateContext()) @@ -406,7 +406,7 @@ public virtual void Method_parameter_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Using_multiple_context_in_filter_parametrize_only_current_context() { using (var context = CreateContext()) diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.QueryTypes.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.QueryTypes.cs index b2e6057076b..54e608a6d0f 100644 --- a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.QueryTypes.cs +++ b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.QueryTypes.cs @@ -56,7 +56,7 @@ public virtual void Auto_initialized_view_set() } } - [ConditionalFact(Skip = "Issue#15264")] + [ConditionalFact(Skip = "Issue#16323")] public virtual void QueryType_with_nav_defining_query() { using (var context = CreateContext()) @@ -70,7 +70,7 @@ var results } } - [ConditionalTheory(Skip = "Issue#15264")] + [ConditionalTheory(Skip = "Issue#16323")] [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_with_defining_query(bool isAsync) { @@ -90,7 +90,7 @@ public virtual Task QueryType_with_defining_query_and_correlated_collection(bool .Select(cv => cv.Orders.Where(cc => true).ToList())); } - [ConditionalTheory(Skip = "Issue#15264")] + [ConditionalTheory(Skip = "Issue#15711")] [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_with_mixed_tracking(bool isAsync) { @@ -107,7 +107,7 @@ from o in ovs.AsNoTracking().Where(ov => ov.CustomerID == c.CustomerID) e => e.c.CustomerID); } - [ConditionalTheory(Skip = "Issue#15264")] + [ConditionalTheory(Skip = "Issue#16323")] [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_with_included_nav(bool isAsync) { @@ -122,7 +122,7 @@ public virtual Task QueryType_with_included_nav(bool isAsync) }); } - [ConditionalTheory(Skip = "Issue#15264")] + [ConditionalTheory(Skip = "Issue#16323")] [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_with_included_navs_multi_level(bool isAsync) { @@ -138,7 +138,7 @@ public virtual Task QueryType_with_included_navs_multi_level(bool isAsync) }); } - [ConditionalTheory(Skip = "Issue#15264")] + [ConditionalTheory(Skip = "Issue#16323")] [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_select_where_navigation(bool isAsync) { @@ -149,7 +149,7 @@ public virtual Task QueryType_select_where_navigation(bool isAsync) select ov); } - [ConditionalTheory(Skip = "Issue#15264")] + [ConditionalTheory(Skip = "Issue#16323")] [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_select_where_navigation_multi_level(bool isAsync) { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FiltersInheritanceSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FiltersInheritanceSqlServerTest.cs index 42991cac641..3a4d8d02903 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FiltersInheritanceSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FiltersInheritanceSqlServerTest.cs @@ -5,8 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 - internal class FiltersInheritanceSqlServerTest : FiltersInheritanceTestBase + public class FiltersInheritanceSqlServerTest : FiltersInheritanceTestBase { public FiltersInheritanceSqlServerTest(FiltersInheritanceSqlServerFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) @@ -22,7 +21,7 @@ public override void Can_use_of_type_animal() AssertSql( @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] FROM [Animal] AS [a] -WHERE [a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1) +WHERE [a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1) ORDER BY [a].[Species]"); } @@ -33,7 +32,7 @@ public override void Can_use_is_kiwi() AssertSql( @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] FROM [Animal] AS [a] -WHERE ([a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1)) AND ([a].[Discriminator] = N'Kiwi')"); +WHERE ([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND ([a].[Discriminator] = N'Kiwi')"); } public override void Can_use_is_kiwi_with_other_predicate() @@ -43,7 +42,7 @@ public override void Can_use_is_kiwi_with_other_predicate() AssertSql( @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] FROM [Animal] AS [a] -WHERE ([a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1)) AND (([a].[Discriminator] = N'Kiwi') AND ([a].[CountryId] = 1))"); +WHERE ([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND (([a].[Discriminator] = N'Kiwi') AND ([a].[CountryId] = 1))"); } public override void Can_use_is_kiwi_in_projection() @@ -52,11 +51,11 @@ public override void Can_use_is_kiwi_in_projection() AssertSql( @"SELECT CASE - WHEN [a].[Discriminator] = N'Kiwi' - THEN CAST(1 AS bit) ELSE CAST(0 AS bit) + WHEN [a].[Discriminator] = N'Kiwi' THEN CAST(1 AS bit) + ELSE CAST(0 AS bit) END FROM [Animal] AS [a] -WHERE [a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1)"); +WHERE [a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)"); } public override void Can_use_of_type_bird() @@ -66,7 +65,7 @@ public override void Can_use_of_type_bird() AssertSql( @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] FROM [Animal] AS [a] -WHERE [a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1) +WHERE ([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND [a].[Discriminator] IN (N'Eagle', N'Kiwi') ORDER BY [a].[Species]"); } @@ -77,7 +76,7 @@ public override void Can_use_of_type_bird_predicate() AssertSql( @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] FROM [Animal] AS [a] -WHERE ([a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1)) AND ([a].[CountryId] = 1) +WHERE (([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND ([a].[CountryId] = 1)) AND [a].[Discriminator] IN (N'Eagle', N'Kiwi') ORDER BY [a].[Species]"); } @@ -88,7 +87,7 @@ public override void Can_use_of_type_bird_with_projection() AssertSql( @"SELECT [a].[EagleId] FROM [Animal] AS [a] -WHERE [a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1)"); +WHERE ([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND [a].[Discriminator] IN (N'Eagle', N'Kiwi')"); } public override void Can_use_of_type_bird_first() @@ -98,7 +97,7 @@ public override void Can_use_of_type_bird_first() AssertSql( @"SELECT TOP(1) [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group], [a].[FoundOn] FROM [Animal] AS [a] -WHERE [a].[Discriminator] IN (N'Kiwi', N'Eagle') AND ([a].[CountryId] = 1) +WHERE ([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND [a].[Discriminator] IN (N'Eagle', N'Kiwi') ORDER BY [a].[Species]"); } @@ -109,7 +108,33 @@ public override void Can_use_of_type_kiwi() AssertSql( @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[FoundOn] FROM [Animal] AS [a] -WHERE ([a].[Discriminator] = N'Kiwi') AND ([a].[CountryId] = 1)"); +WHERE ([a].[Discriminator] IN (N'Eagle', N'Kiwi') AND ([a].[CountryId] = 1)) AND ([a].[Discriminator] = N'Kiwi')"); + } + + public override void Can_use_derived_set() + { + base.Can_use_derived_set(); + + AssertSql( + @"SELECT [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group] +FROM [Animal] AS [a] +WHERE ([a].[Discriminator] = N'Eagle') AND ([a].[CountryId] = 1)"); + } + + public override void Can_use_IgnoreQueryFilters_and_GetDatabaseValues() + { + base.Can_use_IgnoreQueryFilters_and_GetDatabaseValues(); + + AssertSql( + @"SELECT TOP(2) [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group] +FROM [Animal] AS [a] +WHERE [a].[Discriminator] = N'Eagle'", + // + @"@__p_0='Aquila chrysaetos canadensis' (Size = 100) + +SELECT TOP(1) [a].[Species], [a].[CountryId], [a].[Discriminator], [a].[Name], [a].[EagleId], [a].[IsFlightless], [a].[Group] +FROM [Animal] AS [a] +WHERE ([a].[Discriminator] = N'Eagle') AND (([a].[Species] = @__p_0) AND @__p_0 IS NOT NULL)"); } private void AssertSql(params string[] expected) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FiltersSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FiltersSqlServerTest.cs index c0f65310956..a8faf1ff40d 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FiltersSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FiltersSqlServerTest.cs @@ -9,8 +9,7 @@ // ReSharper disable InconsistentNaming namespace Microsoft.EntityFrameworkCore.Query { - //issue #15264 - internal class FiltersSqlServerTest : FiltersTestBase> + public class FiltersSqlServerTest : FiltersTestBase> { public FiltersSqlServerTest(NorthwindQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) @@ -28,7 +27,7 @@ public override void Count_query() SELECT COUNT(*) FROM [Customers] AS [c] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Client_eval() @@ -49,7 +48,7 @@ public override void Materialized_query() SELECT [c].[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] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Find() @@ -62,7 +61,7 @@ public override void Find() SELECT TOP(1) [c].[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] -WHERE (([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')) AND ([c].[CustomerID] = @__p_0)"); +WHERE (((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))) AND (([c].[CustomerID] = @__p_0) AND @__p_0 IS NOT NULL)"); } public override void Materialized_query_parameter() @@ -74,7 +73,7 @@ public override void Materialized_query_parameter() SELECT [c].[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] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Materialized_query_parameter_new_context() @@ -86,13 +85,13 @@ public override void Materialized_query_parameter_new_context() SELECT [c].[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] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')", +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))", // @"@__ef_filter__TenantPrefix_0='T' (Size = 4000) SELECT [c].[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] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Projection_query_parameter() @@ -104,7 +103,7 @@ public override void Projection_query_parameter() SELECT [c].[CustomerID] FROM [Customers] AS [c] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Projection_query() @@ -116,7 +115,7 @@ public override void Projection_query() SELECT [c].[CustomerID] FROM [Customers] AS [c] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Include_query() @@ -126,23 +125,11 @@ public override void Include_query() AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) -SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] +SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Customers] AS [c] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'') -ORDER BY [c].[CustomerID]", - // - @"@__ef_filter__TenantPrefix_1='B' (Size = 4000) - -SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] -FROM [Orders] AS [o] -LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -INNER JOIN ( - SELECT [c0].[CustomerID] - FROM [Customers] AS [c0] - WHERE ([c0].[CompanyName] LIKE @__ef_filter__TenantPrefix_1 + N'%' AND (LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_1)) = @__ef_filter__TenantPrefix_1)) OR (@__ef_filter__TenantPrefix_1 = N'') -) AS [t] ON [o].[CustomerID] = [t].[CustomerID] -WHERE [o.Customer].[CompanyName] IS NOT NULL -ORDER BY [t].[CustomerID]"); +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL))))) +ORDER BY [c].[CustomerID], [o].[OrderID]"); } public override void Include_query_opt_out() @@ -150,17 +137,10 @@ public override void Include_query_opt_out() base.Include_query_opt_out(); AssertSql( - @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region] + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] FROM [Customers] AS [c] -ORDER BY [c].[CustomerID]", - // - @"SELECT [c.Orders].[OrderID], [c.Orders].[CustomerID], [c.Orders].[EmployeeID], [c.Orders].[OrderDate] -FROM [Orders] AS [c.Orders] -INNER JOIN ( - SELECT [c0].[CustomerID] - FROM [Customers] AS [c0] -) AS [t] ON [c.Orders].[CustomerID] = [t].[CustomerID] -ORDER BY [t].[CustomerID]"); +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +ORDER BY [c].[CustomerID], [o].[OrderID]"); } public override void Included_many_to_one_query() @@ -172,13 +152,35 @@ public override void Included_many_to_one_query() SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[CustomerID], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] FROM [Orders] AS [o] -LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] LEFT JOIN ( SELECT [c].[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] - WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'') + WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL))))) ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] -WHERE [o.Customer].[CompanyName] IS NOT NULL"); +WHERE [t].[CompanyName] IS NOT NULL"); + } + + public override void Project_reference_that_itself_has_query_filter_with_another_reference() + { + base.Project_reference_that_itself_has_query_filter_with_another_reference(); + + AssertSql( + @"@__ef_filter__TenantPrefix_1='B' (Size = 4000) +@__ef_filter___quantity_0='50' + +SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] +FROM [Order Details] AS [o0] +INNER JOIN ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [t].[CustomerID] AS [CustomerID0], [t].[Address], [t].[City], [t].[CompanyName], [t].[ContactName], [t].[ContactTitle], [t].[Country], [t].[Fax], [t].[Phone], [t].[PostalCode], [t].[Region] + FROM [Orders] AS [o] + LEFT JOIN ( + SELECT [c].[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] + WHERE ((@__ef_filter__TenantPrefix_1 = N'') AND @__ef_filter__TenantPrefix_1 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_1 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_1)) = @__ef_filter__TenantPrefix_1) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_1)) IS NOT NULL AND @__ef_filter__TenantPrefix_1 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_1)) IS NULL AND @__ef_filter__TenantPrefix_1 IS NULL))))) + ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] + WHERE [t].[CompanyName] IS NOT NULL +) AS [t0] ON [o0].[OrderID] = [t0].[OrderID] +WHERE [o0].[Quantity] > @__ef_filter___quantity_0"); } public override void Included_one_to_many_query_with_client_eval() @@ -224,7 +226,7 @@ WHERE [od].[Quantity] > @__ef_filter___quantity_1 WHERE (([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')) AND ([t0].[Discount] < CAST(10 AS real))"); } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact(Skip = "issue #16326")] public void FromSql_is_composed() { using (var context = CreateContext()) @@ -241,7 +243,7 @@ public void FromSql_is_composed() FROM ( select * from Customers ) AS [c] -WHERE ([c].[CompanyName] LIKE @__ef_filter__TenantPrefix_0 + N'%' AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = N'')"); +WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c].[CompanyName] LIKE [c].[CompanyName] + N'%') AND (((LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL)))))"); } public override void Compiled_query() diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs index 8c81b8f7950..005abe712e3 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryBugsTest.cs @@ -4770,7 +4770,7 @@ public class EmployeeDevice13025 #region Bug12170 - [ConditionalFact] + [ConditionalFact(Skip = "issue #16321")] public virtual void Weak_entities_with_query_filter_subquery_flattening() { using (CreateDatabase12170()) diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs index bf2263537c5..076e1cb05de 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/QueryFilterFuncletizationSqlServerTest.cs @@ -6,8 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 - internal class QueryFilterFuncletizationSqlServerTest + public class QueryFilterFuncletizationSqlServerTest : QueryFilterFuncletizationTestBase { public QueryFilterFuncletizationSqlServerTest( @@ -27,9 +26,9 @@ public override void DbContext_property_parameter_does_not_clash_with_closure_pa @"@__ef_filter__Field_0='False' @__Field_0='False' -SELECT [e].[Id], [e].[IsEnabled] -FROM [FieldFilter] AS [e] -WHERE ([e].[IsEnabled] = @__ef_filter__Field_0) AND ([e].[IsEnabled] = @__Field_0)"); +SELECT [f].[Id], [f].[IsEnabled] +FROM [FieldFilter] AS [f] +WHERE (([f].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL) AND (([f].[IsEnabled] = @__Field_0) AND @__Field_0 IS NOT NULL)"); } public override void DbContext_field_is_parameterized() @@ -39,15 +38,15 @@ public override void DbContext_field_is_parameterized() AssertSql( @"@__ef_filter__Field_0='False' -SELECT [e].[Id], [e].[IsEnabled] -FROM [FieldFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0", +SELECT [f].[Id], [f].[IsEnabled] +FROM [FieldFilter] AS [f] +WHERE ([f].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL", // @"@__ef_filter__Field_0='True' -SELECT [e].[Id], [e].[IsEnabled] -FROM [FieldFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0"); +SELECT [f].[Id], [f].[IsEnabled] +FROM [FieldFilter] AS [f] +WHERE ([f].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL"); } public override void DbContext_property_is_parameterized() @@ -57,15 +56,15 @@ public override void DbContext_property_is_parameterized() AssertSql( @"@__ef_filter__Property_0='False' -SELECT [e].[Id], [e].[IsEnabled] -FROM [PropertyFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Property_0", +SELECT [p].[Id], [p].[IsEnabled] +FROM [PropertyFilter] AS [p] +WHERE ([p].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL", // @"@__ef_filter__Property_0='True' -SELECT [e].[Id], [e].[IsEnabled] -FROM [PropertyFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Property_0"); +SELECT [p].[Id], [p].[IsEnabled] +FROM [PropertyFilter] AS [p] +WHERE ([p].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL"); } public override void DbContext_method_call_is_parameterized() @@ -75,9 +74,9 @@ public override void DbContext_method_call_is_parameterized() AssertSql( @"@__ef_filter__p_0='2' -SELECT [e].[Id], [e].[Tenant] -FROM [MethodCallFilter] AS [e] -WHERE [e].[Tenant] = @__ef_filter__p_0"); +SELECT [m].[Id], [m].[Tenant] +FROM [MethodCallFilter] AS [m] +WHERE ([m].[Tenant] = @__ef_filter__p_0) AND @__ef_filter__p_0 IS NOT NULL"); } public override void DbContext_list_is_parameterized() @@ -85,17 +84,17 @@ public override void DbContext_list_is_parameterized() base.DbContext_list_is_parameterized(); AssertSql( - @"SELECT [e].[Id], [e].[Tenant] -FROM [ListFilter] AS [e] -WHERE 0 = 1", + @"SELECT [l].[Id], [l].[Tenant] +FROM [ListFilter] AS [l] +WHERE CAST(1 AS bit) = CAST(0 AS bit)", // - @"SELECT [e].[Id], [e].[Tenant] -FROM [ListFilter] AS [e] -WHERE [e].[Tenant] IN (1)", + @"SELECT [l].[Id], [l].[Tenant] +FROM [ListFilter] AS [l] +WHERE [l].[Tenant] IN (1)", // - @"SELECT [e].[Id], [e].[Tenant] -FROM [ListFilter] AS [e] -WHERE [e].[Tenant] IN (2, 3)"); + @"SELECT [l].[Id], [l].[Tenant] +FROM [ListFilter] AS [l] +WHERE [l].[Tenant] IN (2, 3)"); } public override void DbContext_property_chain_is_parameterized() @@ -105,15 +104,15 @@ public override void DbContext_property_chain_is_parameterized() AssertSql( @"@__ef_filter__Enabled_0='False' -SELECT [e].[Id], [e].[IsEnabled] -FROM [PropertyChainFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Enabled_0", +SELECT [p].[Id], [p].[IsEnabled] +FROM [PropertyChainFilter] AS [p] +WHERE ([p].[IsEnabled] = @__ef_filter__Enabled_0) AND @__ef_filter__Enabled_0 IS NOT NULL", // @"@__ef_filter__Enabled_0='True' -SELECT [e].[Id], [e].[IsEnabled] -FROM [PropertyChainFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Enabled_0"); +SELECT [p].[Id], [p].[IsEnabled] +FROM [PropertyChainFilter] AS [p] +WHERE ([p].[IsEnabled] = @__ef_filter__Enabled_0) AND @__ef_filter__Enabled_0 IS NOT NULL"); } public override void DbContext_property_method_call_is_parameterized() @@ -123,9 +122,9 @@ public override void DbContext_property_method_call_is_parameterized() AssertSql( @"@__ef_filter__p_0='2' -SELECT [e].[Id], [e].[Tenant] -FROM [PropertyMethodCallFilter] AS [e] -WHERE [e].[Tenant] = @__ef_filter__p_0"); +SELECT [p].[Id], [p].[Tenant] +FROM [PropertyMethodCallFilter] AS [p] +WHERE ([p].[Tenant] = @__ef_filter__p_0) AND @__ef_filter__p_0 IS NOT NULL"); } public override void DbContext_method_call_chain_is_parameterized() @@ -135,9 +134,9 @@ public override void DbContext_method_call_chain_is_parameterized() AssertSql( @"@__ef_filter__p_0='2' -SELECT [e].[Id], [e].[Tenant] -FROM [MethodCallChainFilter] AS [e] -WHERE [e].[Tenant] = @__ef_filter__p_0"); +SELECT [m].[Id], [m].[Tenant] +FROM [MethodCallChainFilter] AS [m] +WHERE ([m].[Tenant] = @__ef_filter__p_0) AND @__ef_filter__p_0 IS NOT NULL"); } public override void DbContext_complex_expression_is_parameterized() @@ -148,23 +147,23 @@ public override void DbContext_complex_expression_is_parameterized() @"@__ef_filter__Property_0='False' @__ef_filter__p_1='True' -SELECT [x].[Id], [x].[IsEnabled] -FROM [ComplexFilter] AS [x] -WHERE ([x].[IsEnabled] = @__ef_filter__Property_0) AND (@__ef_filter__p_1 = CAST(1 AS bit))", +SELECT [c].[Id], [c].[IsEnabled] +FROM [ComplexFilter] AS [c] +WHERE (([c].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND (@__ef_filter__p_1 = CAST(1 AS bit))", // @"@__ef_filter__Property_0='True' @__ef_filter__p_1='True' -SELECT [x].[Id], [x].[IsEnabled] -FROM [ComplexFilter] AS [x] -WHERE ([x].[IsEnabled] = @__ef_filter__Property_0) AND (@__ef_filter__p_1 = CAST(1 AS bit))", +SELECT [c].[Id], [c].[IsEnabled] +FROM [ComplexFilter] AS [c] +WHERE (([c].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND (@__ef_filter__p_1 = CAST(1 AS bit))", // @"@__ef_filter__Property_0='True' @__ef_filter__p_1='False' -SELECT [x].[Id], [x].[IsEnabled] -FROM [ComplexFilter] AS [x] -WHERE ([x].[IsEnabled] = @__ef_filter__Property_0) AND (@__ef_filter__p_1 = CAST(1 AS bit))"); +SELECT [c].[Id], [c].[IsEnabled] +FROM [ComplexFilter] AS [c] +WHERE (([c].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND (@__ef_filter__p_1 = CAST(1 AS bit))"); } public override void DbContext_property_based_filter_does_not_short_circuit() @@ -175,22 +174,23 @@ public override void DbContext_property_based_filter_does_not_short_circuit() @"@__ef_filter__p_0='False' @__ef_filter__IsModerated_1='True' (Nullable = true) -SELECT [x].[Id], [x].[IsDeleted], [x].[IsModerated] -FROM [ShortCircuitFilter] AS [x] -WHERE ([x].[IsDeleted] = CAST(0 AS bit)) AND ((@__ef_filter__p_0 = CAST(1 AS bit)) OR (@__ef_filter__IsModerated_1 = [x].[IsModerated]))", +SELECT [s].[Id], [s].[IsDeleted], [s].[IsModerated] +FROM [ShortCircuitFilter] AS [s] +WHERE ([s].[IsDeleted] <> CAST(1 AS bit)) AND ((@__ef_filter__p_0 = CAST(1 AS bit)) OR ((@__ef_filter__IsModerated_1 = [s].[IsModerated]) AND @__ef_filter__IsModerated_1 IS NOT NULL))", // @"@__ef_filter__p_0='False' @__ef_filter__IsModerated_1='False' (Nullable = true) -SELECT [x].[Id], [x].[IsDeleted], [x].[IsModerated] -FROM [ShortCircuitFilter] AS [x] -WHERE ([x].[IsDeleted] = CAST(0 AS bit)) AND ((@__ef_filter__p_0 = CAST(1 AS bit)) OR (@__ef_filter__IsModerated_1 = [x].[IsModerated]))", +SELECT [s].[Id], [s].[IsDeleted], [s].[IsModerated] +FROM [ShortCircuitFilter] AS [s] +WHERE ([s].[IsDeleted] <> CAST(1 AS bit)) AND ((@__ef_filter__p_0 = CAST(1 AS bit)) OR ((@__ef_filter__IsModerated_1 = [s].[IsModerated]) AND @__ef_filter__IsModerated_1 IS NOT NULL))", // @"@__ef_filter__p_0='True' +@__ef_filter__IsModerated_1='' -SELECT [x].[Id], [x].[IsDeleted], [x].[IsModerated] -FROM [ShortCircuitFilter] AS [x] -WHERE ([x].[IsDeleted] = CAST(0 AS bit)) AND ((@__ef_filter__p_0 = CAST(1 AS bit)) OR [x].[IsModerated] IS NULL)"); +SELECT [s].[Id], [s].[IsDeleted], [s].[IsModerated] +FROM [ShortCircuitFilter] AS [s] +WHERE ([s].[IsDeleted] <> CAST(1 AS bit)) AND ((@__ef_filter__p_0 = CAST(1 AS bit)) OR ((@__ef_filter__IsModerated_1 = [s].[IsModerated]) AND @__ef_filter__IsModerated_1 IS NOT NULL))"); } public override void EntityTypeConfiguration_DbContext_field_is_parameterized() @@ -202,13 +202,13 @@ public override void EntityTypeConfiguration_DbContext_field_is_parameterized() SELECT [e].[Id], [e].[IsEnabled] FROM [EntityTypeConfigurationFieldFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0", +WHERE ([e].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL", // @"@__ef_filter__Field_0='True' SELECT [e].[Id], [e].[IsEnabled] FROM [EntityTypeConfigurationFieldFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0"); +WHERE ([e].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL"); } public override void EntityTypeConfiguration_DbContext_property_is_parameterized() @@ -220,13 +220,13 @@ public override void EntityTypeConfiguration_DbContext_property_is_parameterized SELECT [e].[Id], [e].[IsEnabled] FROM [EntityTypeConfigurationPropertyFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Property_0", +WHERE ([e].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL", // @"@__ef_filter__Property_0='True' SELECT [e].[Id], [e].[IsEnabled] FROM [EntityTypeConfigurationPropertyFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Property_0"); +WHERE ([e].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL"); } public override void EntityTypeConfiguration_DbContext_method_call_is_parameterized() @@ -238,7 +238,7 @@ public override void EntityTypeConfiguration_DbContext_method_call_is_parameteri SELECT [e].[Id], [e].[Tenant] FROM [EntityTypeConfigurationMethodCallFilter] AS [e] -WHERE [e].[Tenant] = @__ef_filter__p_0"); +WHERE ([e].[Tenant] = @__ef_filter__p_0) AND @__ef_filter__p_0 IS NOT NULL"); } public override void EntityTypeConfiguration_DbContext_property_chain_is_parameterized() @@ -250,13 +250,13 @@ public override void EntityTypeConfiguration_DbContext_property_chain_is_paramet SELECT [e].[Id], [e].[IsEnabled] FROM [EntityTypeConfigurationPropertyChainFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Enabled_0", +WHERE ([e].[IsEnabled] = @__ef_filter__Enabled_0) AND @__ef_filter__Enabled_0 IS NOT NULL", // @"@__ef_filter__Enabled_0='True' SELECT [e].[Id], [e].[IsEnabled] FROM [EntityTypeConfigurationPropertyChainFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Enabled_0"); +WHERE ([e].[IsEnabled] = @__ef_filter__Enabled_0) AND @__ef_filter__Enabled_0 IS NOT NULL"); } public override void Local_method_DbContext_field_is_parameterized() @@ -266,15 +266,15 @@ public override void Local_method_DbContext_field_is_parameterized() AssertSql( @"@__ef_filter__Field_0='False' -SELECT [e].[Id], [e].[IsEnabled] -FROM [LocalMethodFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0", +SELECT [l].[Id], [l].[IsEnabled] +FROM [LocalMethodFilter] AS [l] +WHERE ([l].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL", // @"@__ef_filter__Field_0='True' -SELECT [e].[Id], [e].[IsEnabled] -FROM [LocalMethodFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0"); +SELECT [l].[Id], [l].[IsEnabled] +FROM [LocalMethodFilter] AS [l] +WHERE ([l].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL"); } public override void Local_static_method_DbContext_property_is_parameterized() @@ -284,15 +284,15 @@ public override void Local_static_method_DbContext_property_is_parameterized() AssertSql( @"@__ef_filter__Property_0='False' -SELECT [e].[Id], [e].[IsEnabled] -FROM [LocalMethodParamsFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Property_0", +SELECT [l].[Id], [l].[IsEnabled] +FROM [LocalMethodParamsFilter] AS [l] +WHERE ([l].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL", // @"@__ef_filter__Property_0='True' -SELECT [e].[Id], [e].[IsEnabled] -FROM [LocalMethodParamsFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Property_0"); +SELECT [l].[Id], [l].[IsEnabled] +FROM [LocalMethodParamsFilter] AS [l] +WHERE ([l].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL"); } public override void Remote_method_DbContext_property_method_call_is_parameterized() @@ -302,9 +302,9 @@ public override void Remote_method_DbContext_property_method_call_is_parameteriz AssertSql( @"@__ef_filter__p_0='2' -SELECT [e].[Id], [e].[Tenant] -FROM [RemoteMethodParamsFilter] AS [e] -WHERE [e].[Tenant] = @__ef_filter__p_0"); +SELECT [r].[Id], [r].[Tenant] +FROM [RemoteMethodParamsFilter] AS [r] +WHERE ([r].[Tenant] = @__ef_filter__p_0) AND @__ef_filter__p_0 IS NOT NULL"); } public override void Extension_method_DbContext_field_is_parameterized() @@ -316,13 +316,13 @@ public override void Extension_method_DbContext_field_is_parameterized() SELECT [e].[Id], [e].[IsEnabled] FROM [ExtensionBuilderFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0", +WHERE ([e].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL", // @"@__ef_filter__Field_0='True' SELECT [e].[Id], [e].[IsEnabled] FROM [ExtensionBuilderFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Field_0"); +WHERE ([e].[IsEnabled] = @__ef_filter__Field_0) AND @__ef_filter__Field_0 IS NOT NULL"); } public override void Extension_method_DbContext_property_chain_is_parameterized() @@ -334,13 +334,13 @@ public override void Extension_method_DbContext_property_chain_is_parameterized( SELECT [e].[Id], [e].[IsEnabled] FROM [ExtensionContextFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Enabled_0", +WHERE ([e].[IsEnabled] = @__ef_filter__Enabled_0) AND @__ef_filter__Enabled_0 IS NOT NULL", // @"@__ef_filter__Enabled_0='True' SELECT [e].[Id], [e].[IsEnabled] FROM [ExtensionContextFilter] AS [e] -WHERE [e].[IsEnabled] = @__ef_filter__Enabled_0"); +WHERE ([e].[IsEnabled] = @__ef_filter__Enabled_0) AND @__ef_filter__Enabled_0 IS NOT NULL"); } public override void Using_DbSet_in_filter_works() @@ -374,9 +374,9 @@ public override void Static_member_from_dbContext_is_inlined() base.Static_member_from_dbContext_is_inlined(); AssertSql( - @"SELECT [e].[Id], [e].[UserId] -FROM [DbContextStaticMemberFilter] AS [e] -WHERE [e].[UserId] <> 1"); + @"SELECT [d].[Id], [d].[UserId] +FROM [DbContextStaticMemberFilter] AS [d] +WHERE [d].[UserId] <> 1"); } public override void Static_member_from_non_dbContext_is_inlined() @@ -384,9 +384,9 @@ public override void Static_member_from_non_dbContext_is_inlined() base.Static_member_from_non_dbContext_is_inlined(); AssertSql( - @"SELECT [b].[Id], [b].[IsEnabled] -FROM [StaticMemberFilter] AS [b] -WHERE [b].[IsEnabled] = CAST(1 AS bit)"); + @"SELECT [s].[Id], [s].[IsEnabled] +FROM [StaticMemberFilter] AS [s] +WHERE [s].[IsEnabled] = CAST(1 AS bit)"); } public override void Local_variable_from_OnModelCreating_is_inlined() @@ -394,9 +394,9 @@ public override void Local_variable_from_OnModelCreating_is_inlined() base.Local_variable_from_OnModelCreating_is_inlined(); AssertSql( - @"SELECT [e].[Id], [e].[IsEnabled] -FROM [LocalVariableFilter] AS [e] -WHERE [e].[IsEnabled] = CAST(1 AS bit)"); + @"SELECT [l].[Id], [l].[IsEnabled] +FROM [LocalVariableFilter] AS [l] +WHERE [l].[IsEnabled] = CAST(1 AS bit)"); } public override void Method_parameter_is_inlined() @@ -404,9 +404,9 @@ public override void Method_parameter_is_inlined() base.Method_parameter_is_inlined(); AssertSql( - @"SELECT [e].[Id], [e].[Tenant] -FROM [ParameterFilter] AS [e] -WHERE [e].[Tenant] = 0"); + @"SELECT [p].[Id], [p].[Tenant] +FROM [ParameterFilter] AS [p] +WHERE [p].[Tenant] = 0"); } public override void Using_multiple_context_in_filter_parametrize_only_current_context() @@ -416,15 +416,15 @@ public override void Using_multiple_context_in_filter_parametrize_only_current_c AssertSql( @"@__ef_filter__Property_0='False' -SELECT [e].[Id], [e].[BossId], [e].[IsEnabled] -FROM [MultiContextFilter] AS [e] -WHERE ([e].[IsEnabled] = @__ef_filter__Property_0) AND ([e].[BossId] = 1)", +SELECT [m].[Id], [m].[BossId], [m].[IsEnabled] +FROM [MultiContextFilter] AS [m] +WHERE (([m].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND ([m].[BossId] = 1)", // @"@__ef_filter__Property_0='True' -SELECT [e].[Id], [e].[BossId], [e].[IsEnabled] -FROM [MultiContextFilter] AS [e] -WHERE ([e].[IsEnabled] = @__ef_filter__Property_0) AND ([e].[BossId] = 1)"); +SELECT [m].[Id], [m].[BossId], [m].[IsEnabled] +FROM [MultiContextFilter] AS [m] +WHERE (([m].[IsEnabled] = @__ef_filter__Property_0) AND @__ef_filter__Property_0 IS NOT NULL) AND ([m].[BossId] = 1)"); } private void AssertSql(params string[] expected) diff --git a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs index fdae32b8bd2..d6578027928 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs @@ -16,10 +16,7 @@ public class SqlServerComplianceTest : RelationalComplianceTestBase typeof(GraphUpdatesTestBase<>), // issue #15318 typeof(ProxyGraphUpdatesTestBase<>), // issue #15318 typeof(ComplexNavigationsWeakQueryTestBase<>), // issue #15285 - typeof(FiltersInheritanceTestBase<>), // issue #15264 - typeof(FiltersTestBase<>), // issue #15264 typeof(OwnedQueryTestBase<>), // issue #15285 - typeof(QueryFilterFuncletizationTestBase<>), // issue #15264 typeof(RelationalOwnedQueryTestBase<>), // issue #15285 // Query pipeline typeof(ConcurrencyDetectorTestBase<>), diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/FiltersInheritanceSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/FiltersInheritanceSqliteTest.cs index 3a5364acb54..8f3491775b7 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/FiltersInheritanceSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/FiltersInheritanceSqliteTest.cs @@ -3,8 +3,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 - internal class FiltersInheritanceSqliteTest : FiltersInheritanceTestBase + public class FiltersInheritanceSqliteTest : FiltersInheritanceTestBase { public FiltersInheritanceSqliteTest(FiltersInheritanceSqliteFixture fixture) : base(fixture) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/FiltersSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/FiltersSqliteTest.cs index 9caa9380e4a..432664208d0 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/FiltersSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/FiltersSqliteTest.cs @@ -5,8 +5,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 - internal class FiltersSqliteTest : FiltersTestBase> + public class FiltersSqliteTest : FiltersTestBase> { public FiltersSqliteTest(NorthwindQuerySqliteFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) @@ -18,12 +17,13 @@ public FiltersSqliteTest(NorthwindQuerySqliteFixture public override void Count_query() { base.Count_query(); + AssertSql( @"@__ef_filter__TenantPrefix_0='B' (Size = 1) SELECT COUNT(*) FROM ""Customers"" AS ""c"" -WHERE (""c"".""CompanyName"" LIKE @__ef_filter__TenantPrefix_0 || '%' AND (substr(""c"".""CompanyName"", 1, length(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0)) OR (@__ef_filter__TenantPrefix_0 = '')"); +WHERE ((@__ef_filter__TenantPrefix_0 = '') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR (""c"".""CompanyName"" IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (((""c"".""CompanyName"" LIKE ""c"".""CompanyName"" || '%') AND (((substr(""c"".""CompanyName"", 1, length(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (substr(""c"".""CompanyName"", 1, length(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (substr(""c"".""CompanyName"", 1, length(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL))) OR ((@__ef_filter__TenantPrefix_0 = '') AND @__ef_filter__TenantPrefix_0 IS NOT NULL))))"); } private void AssertSql(params string[] expected) diff --git a/test/EFCore.Sqlite.FunctionalTests/Query/QueryFilterFuncletizationSqliteTest.cs b/test/EFCore.Sqlite.FunctionalTests/Query/QueryFilterFuncletizationSqliteTest.cs index 2af7d0ba001..85df1031c29 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/QueryFilterFuncletizationSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/QueryFilterFuncletizationSqliteTest.cs @@ -6,8 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 - internal class QueryFilterFuncletizationSqliteTest + public class QueryFilterFuncletizationSqliteTest : QueryFilterFuncletizationTestBase { public QueryFilterFuncletizationSqliteTest( diff --git a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs index 80fb5047c77..e122380831a 100644 --- a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs @@ -20,10 +20,7 @@ public class SqliteComplianceTest : RelationalComplianceTestBase typeof(GraphUpdatesTestBase<>), // issue #15318 typeof(ProxyGraphUpdatesTestBase<>), // issue #15318 typeof(ComplexNavigationsWeakQueryTestBase<>), // issue #15285 - typeof(FiltersInheritanceTestBase<>), // issue #15264 - typeof(FiltersTestBase<>), // issue #15264 typeof(OwnedQueryTestBase<>), // issue #15285 - typeof(QueryFilterFuncletizationTestBase<>), // issue #15264 typeof(RelationalOwnedQueryTestBase<>), // issue #15285 // Query pipeline typeof(ConcurrencyDetectorTestBase<>),