From bb5a176f3293b1da986a537942d6de3ce4cbac1b Mon Sep 17 00:00:00 2001 From: Maurycy Markowski Date: Mon, 5 Aug 2019 16:51:43 -0700 Subject: [PATCH] Fix to #15264 - QueryRewrite: incorporate query filters into nav rewrite When nav rewrite constructs new EntityQueryable it now looks into EntityType for any query filter annotations and applies them on top. Those predicates are parameterized and the parameters created are injected into the context as part of query execution expression. Generated predicate expression is re-visited by nav expansion to recursively handle filters (expand potential navigation expansions that were introduced in them). This adds some complexity to the way navigations are expanded - before the newly constructed join always was guaranteed to be an entity. Now it can be a complex structure with multiple navigations already expanded. Also small cleanup on ExpressionPrinter - adding IPrintable to some of the custom nodes created by nav expansion and removing rendering of connecting lines when printing the expression. --- src/EFCore/Internal/EntityFinder.cs | 2 +- src/EFCore/Internal/IndentedStringBuilder.cs | 103 +-------- src/EFCore/Query/ExpressionPrinter.cs | 99 +++++---- ...ingExpressionVisitor.ExpressionVisitors.cs | 24 +- ...nExpandingExpressionVisitor.Expressions.cs | 53 ++++- .../NavigationExpandingExpressionVisitor.cs | 119 +++++++++- ...ueryMetadataExtractingExpressionVisitor.cs | 9 + src/EFCore/Query/QueryCompilationContext.cs | 1 + src/EFCore/Query/QueryOptimizer.cs | 2 +- .../Query/QueryOptimizerDependencies.cs | 22 +- .../Query/ReplacingExpressionVisitor.cs | 1 - .../InMemoryComplianceTest.cs | 3 +- .../Query/FiltersInMemoryTest.cs | 22 +- .../Query/FiltersInheritanceInMemoryTest.cs | 2 +- .../Query/SimpleQueryInMemoryTest.cs | 12 + .../Query/FiltersInheritanceTestBase.cs | 22 +- .../Query/FiltersTestBase.cs | 40 ++-- .../QueryFilterFuncletizationTestBase.cs | 53 +++-- .../Query/SimpleQueryTestBase.QueryTypes.cs | 20 +- .../ProceduralQueryExpressionGenerator.cs | 2 +- .../Query/FiltersInheritanceSqlServerTest.cs | 25 +-- .../Query/FiltersSqlServerTest.cs | 117 +++++----- .../QueryFilterFuncletizationSqlServerTest.cs | 206 +++++++++--------- .../SqlServerComplianceTest.cs | 3 - .../Query/FiltersInheritanceSqliteTest.cs | 3 +- .../Query/FiltersSqliteTest.cs | 6 +- .../QueryFilterFuncletizationSqliteTest.cs | 4 +- .../SqliteComplianceTest.cs | 3 - 28 files changed, 546 insertions(+), 432 deletions(-) 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/Internal/IndentedStringBuilder.cs b/src/EFCore/Internal/IndentedStringBuilder.cs index 6d1925bb1fc..6312a4b84eb 100644 --- a/src/EFCore/Internal/IndentedStringBuilder.cs +++ b/src/EFCore/Internal/IndentedStringBuilder.cs @@ -2,7 +2,6 @@ // 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.IO; using System.Text; using JetBrains.Annotations; @@ -18,11 +17,6 @@ namespace Microsoft.EntityFrameworkCore.Internal public class IndentedStringBuilder { private const byte IndentSize = 4; - - private readonly string _disconnectedIndent = new string(' ', IndentSize); - private readonly string _suspendedIndent = "|" + new string(' ', IndentSize - 1); - private readonly string _connectedIndent = "|" + new string('_', IndentSize - 2) + " "; - private byte _indent; private bool _indentPending = true; @@ -158,15 +152,6 @@ public virtual IndentedStringBuilder Clear() return this; } - private enum NodeConnectionState - { - Disconnected = 0, - Connected = 1, - Suspended = 2 - } - - private readonly List _nodeConnectionStates = new List(); - /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -174,78 +159,12 @@ private enum NodeConnectionState /// doing so can result in application failures when updating to a new Entity Framework Core release. /// public virtual IndentedStringBuilder IncrementIndent() - => IncrementIndent(false); - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual IndentedStringBuilder IncrementIndent(bool connectNode) { - var state = connectNode ? NodeConnectionState.Connected : NodeConnectionState.Disconnected; - if (_indent == _nodeConnectionStates.Count) - { - _nodeConnectionStates.Add(state); - } - else - { - _nodeConnectionStates[_indent] = state; - } - _indent++; return this; } - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual void DisconnectCurrentNode() - { - if (_indent > 0 - && _nodeConnectionStates.Count >= _indent) - { - _nodeConnectionStates[_indent - 1] = NodeConnectionState.Disconnected; - } - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual void SuspendCurrentNode() - { - if (_indent > 0 - && _nodeConnectionStates.Count >= _indent - && _nodeConnectionStates[_indent - 1] == NodeConnectionState.Connected) - { - _nodeConnectionStates[_indent - 1] = NodeConnectionState.Suspended; - } - } - - /// - /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to - /// the same compatibility standards as public APIs. It may be changed or removed without notice in - /// any release. You should only use it directly in your code with extreme caution and knowing that - /// doing so can result in application failures when updating to a new Entity Framework Core release. - /// - public virtual void ReconnectCurrentNode() - { - if (_indent > 0 - && _nodeConnectionStates.Count >= _indent - && _nodeConnectionStates[_indent - 1] == NodeConnectionState.Suspended) - { - _nodeConnectionStates[_indent - 1] = NodeConnectionState.Connected; - } - } - /// /// This is an internal API that supports the Entity Framework Core infrastructure and not subject to /// the same compatibility standards as public APIs. It may be changed or removed without notice in @@ -257,8 +176,6 @@ public virtual IndentedStringBuilder DecrementIndent() if (_indent > 0) { _indent--; - - _nodeConnectionStates.RemoveAt(_indent); } return this; @@ -284,25 +201,7 @@ private void DoIndent() { if (_indentPending && (_indent > 0)) { - var indentString = string.Empty; - for (var i = 0; i < _nodeConnectionStates.Count; i++) - { - if (_nodeConnectionStates[i] == NodeConnectionState.Disconnected) - { - indentString += _disconnectedIndent; - } - else if (i == _nodeConnectionStates.Count - 1 - && _nodeConnectionStates[i] == NodeConnectionState.Connected) - { - indentString += _connectedIndent; - } - else - { - indentString += _suspendedIndent; - } - } - - _stringBuilder.Append(indentString); + _stringBuilder.Append(new string(' ', _indent * IndentSize)); } _indentPending = false; diff --git a/src/EFCore/Query/ExpressionPrinter.cs b/src/EFCore/Query/ExpressionPrinter.cs index 60868658ba6..e6fbda1460f 100644 --- a/src/EFCore/Query/ExpressionPrinter.cs +++ b/src/EFCore/Query/ExpressionPrinter.cs @@ -19,6 +19,7 @@ public class ExpressionPrinter : ExpressionVisitor private readonly IndentedStringBuilder _stringBuilder; private readonly Dictionary _parametersInScope; private readonly List _namelessParameters; + private readonly List _encounteredParameters; private readonly Dictionary _binaryOperandMap = new Dictionary { @@ -47,6 +48,7 @@ public ExpressionPrinter() _stringBuilder = new IndentedStringBuilder(); _parametersInScope = new Dictionary(); _namelessParameters = new List(); + _encounteredParameters = new List(); } public virtual IndentedStringBuilder StringBuilder => _stringBuilder; @@ -55,7 +57,7 @@ public ExpressionPrinter() private int? CharacterLimit { get; set; } - private bool PrintConnections { get; set; } + private bool GenerateUniqueParameterIds { get; set; } public virtual void VisitList( IReadOnlyList items, @@ -105,12 +107,6 @@ public virtual ExpressionPrinter IncrementIndent() return this; } - public virtual ExpressionPrinter IncrementIndent(bool connectNode) - { - _stringBuilder.IncrementIndent(connectNode); - return this; - } - public virtual ExpressionPrinter DecrementIndent() { _stringBuilder.DecrementIndent(); @@ -132,18 +128,32 @@ private void AppendLine([NotNull] string message) } public virtual string Print( + Expression expression, + bool removeFormatting = false, + int? characterLimit = null) + => PrintCore(expression, removeFormatting, characterLimit, generateUniqueParameterIds: false); + + public virtual string PrintDebug( Expression expression, bool removeFormatting = false, int? characterLimit = null, - bool printConnections = true) + bool generateUniqueParameterIds = true) + => PrintCore(expression, removeFormatting, characterLimit, generateUniqueParameterIds); + + protected virtual string PrintCore( + Expression expression, + bool removeFormatting, + int? characterLimit, + bool generateUniqueParameterIds) { _stringBuilder.Clear(); _parametersInScope.Clear(); _namelessParameters.Clear(); + _encounteredParameters.Clear(); RemoveFormatting = removeFormatting; CharacterLimit = characterLimit; - PrintConnections = printConnections; + GenerateUniqueParameterIds = generateUniqueParameterIds; Visit(expression); @@ -318,12 +328,6 @@ protected override Expression VisitBinary(BinaryExpression binaryExpression) protected override Expression VisitBlock(BlockExpression blockExpression) { AppendLine(); - - if (PrintConnections) - { - _stringBuilder.SuspendCurrentNode(); - } - AppendLine("{"); _stringBuilder.IncrementIndent(); @@ -359,11 +363,6 @@ protected override Expression VisitBlock(BlockExpression blockExpression) _stringBuilder.DecrementIndent(); Append("}"); - if (PrintConnections) - { - _stringBuilder.ReconnectCurrentNode(); - } - return blockExpression; } @@ -384,11 +383,6 @@ protected override Expression VisitConditional(ConditionalExpression conditional protected override Expression VisitConstant(ConstantExpression constantExpression) { - if (PrintConnections) - { - _stringBuilder.SuspendCurrentNode(); - } - if (constantExpression.Value is IPrintable printable) { printable.Print(this); @@ -402,11 +396,6 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio Print(constantExpression.Value); } - if (PrintConnections) - { - _stringBuilder.ReconnectCurrentNode(); - } - return constantExpression; } @@ -475,6 +464,20 @@ protected override Expression VisitLambda(Expression lambdaExpression) } _stringBuilder.Append(parameter.Type.ShortDisplayName() + " " + parameterName); + if (GenerateUniqueParameterIds) + { + var parameterIndex = _encounteredParameters.Count; + if (_encounteredParameters.Contains(parameter)) + { + parameterIndex = _encounteredParameters.IndexOf(parameter); + } + else + { + _encounteredParameters.Add(parameter); + } + + _stringBuilder.Append("{" + parameterIndex + "}"); + } if (parameter != lambdaExpression.Parameters.Last()) { @@ -620,8 +623,7 @@ var argumentNames if (!isSimpleMethodOrProperty) { - var shouldPrintConnections = PrintConnections && !_nonConnectableMethods.Contains(methodCallExpression.Method.Name); - _stringBuilder.IncrementIndent(shouldPrintConnections); + _stringBuilder.IncrementIndent(); } for (var i = 0; i < methodCallExpression.Arguments.Count; i++) @@ -633,12 +635,6 @@ var argumentNames _stringBuilder.Append(argumentNames[i] + ": "); } - if (i == methodCallExpression.Arguments.Count - 1 - && !isSimpleMethodOrProperty) - { - _stringBuilder.DisconnectCurrentNode(); - } - Visit(argument); if (i < methodCallExpression.Arguments.Count - 1) @@ -715,12 +711,6 @@ protected override Expression VisitNewArray(NewArrayExpression newArrayExpressio var appendAction = isComplex ? (Action)AppendLine : Append; appendAction("new " + newArrayExpression.Type.GetElementType().ShortDisplayName() + "[]"); - - if (PrintConnections) - { - _stringBuilder.SuspendCurrentNode(); - } - appendAction("{ "); if (isComplex) @@ -737,11 +727,6 @@ protected override Expression VisitNewArray(NewArrayExpression newArrayExpressio Append("}"); - if (PrintConnections) - { - _stringBuilder.ReconnectCurrentNode(); - } - return newArrayExpression; } @@ -779,6 +764,21 @@ protected override Expression VisitParameter(ParameterExpression parameterExpres Append(")"); } + if (GenerateUniqueParameterIds) + { + var parameterIndex = _encounteredParameters.Count; + if (_encounteredParameters.Contains(parameterExpression)) + { + parameterIndex = _encounteredParameters.IndexOf(parameterExpression); + } + else + { + _encounteredParameters.Add(parameterExpression); + } + + _stringBuilder.Append("{" + parameterIndex + "}"); + } + return parameterExpression; } @@ -893,7 +893,6 @@ private void VisitArguments(IReadOnlyList arguments, Action if (areConnected && i == arguments.Count - 1) { Append(""); - _stringBuilder.DisconnectCurrentNode(); } Visit(arguments[i]); diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index 046efa8e38b..8841807269f 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -170,7 +170,8 @@ protected Expression ExpandNavigation( innerEntityReference.SetIncludePaths(innerIncludeTreeNode); } - var innerParameter = Expression.Parameter(innerSource.SourceElementType, "i"); + var innerSoureSequenceType = innerSource.Type.GetSequenceType(); + var innerParameter = Expression.Parameter(innerSoureSequenceType, "i"); Expression outerKey; if (root is NavigationExpansionExpression innerNavigationExpansionExpression && innerNavigationExpansionExpression.CardinalityReducingGenericMethodInfo != null) @@ -218,15 +219,12 @@ protected Expression ExpandNavigation( { // This is intentionally deferred to be applied to innerSource.Source // Since outerKey's reference could change if a reference navigation is expanded afterwards - var correlationPredicate = _navigationExpandingExpressionVisitor.ExpandNavigationsInLambdaExpression( - innerSource, - Expression.Lambda(Expression.Equal(outerKey, innerKey), innerParameter)); - var subquery = Expression.Call( - QueryableMethodProvider.WhereMethodInfo.MakeGenericMethod(innerSource.SourceElementType), - innerSource, - Expression.Quote( - Expression.Lambda(correlationPredicate, innerSource.CurrentParameter))); + QueryableMethodProvider.WhereMethodInfo.MakeGenericMethod(innerSoureSequenceType), + innerSource, + Expression.Quote( + Expression.Lambda( + Expression.Equal(outerKey, innerKey), innerParameter))); return new MaterializeCollectionNavigationExpression(subquery, navigation); } @@ -241,8 +239,8 @@ protected Expression ExpandNavigation( innerSource.CurrentParameter); var resultSelectorOuterParameter = Expression.Parameter(_source.SourceElementType, "o"); - var resultSelectorInnerParameter = Expression.Parameter(innerQueryableType, "i"); - var resultType = TransparentIdentifierFactory.Create(_source.SourceElementType, innerQueryableType); + var resultSelectorInnerParameter = Expression.Parameter(innerSource.SourceElementType, "i"); + var resultType = TransparentIdentifierFactory.Create(_source.SourceElementType, innerSource.SourceElementType); var transparentIdentifierOuterMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Outer"); var transparentIdentifierInnerMemberInfo = resultType.GetTypeInfo().GetDeclaredField("Inner"); @@ -269,11 +267,11 @@ protected Expression ExpandNavigation( ? QueryableMethodProvider.JoinMethodInfo : QueryableExtensions.LeftJoinMethodInfo).MakeGenericMethod( _source.SourceElementType, - innerQueryableType, + innerSource.SourceElementType, outerKeySelector.ReturnType, resultSelector.ReturnType), _source.Source, - innerQueryable, + innerSource.Source, outerKeySelector, innerKeySelector, resultSelector)); diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs index 3e08372144e..635c2d99043 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.Expressions.cs @@ -12,7 +12,7 @@ namespace Microsoft.EntityFrameworkCore.Query.Internal { public partial class NavigationExpandingExpressionVisitor { - public class NavigationExpansionExpression : Expression + public class NavigationExpansionExpression : Expression, IPrintable { private readonly List<(MethodInfo OrderingMethod, Expression KeySelector)> _pendingOrderings = new List<(MethodInfo OrderingMethod, Expression KeySelector)>(); @@ -81,13 +81,31 @@ public virtual void ConvertToSingleResult(MethodInfo genericMethod) protected override Expression VisitChildren(ExpressionVisitor visitor) => this; + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.StringBuilder.AppendLine(nameof(NavigationExpansionExpression)); + using (expressionPrinter.Indent()) + { + expressionPrinter.StringBuilder.Append("Source: "); + expressionPrinter.Visit(Source); + expressionPrinter.StringBuilder.AppendLine(); + expressionPrinter.StringBuilder.Append("PendingSelector: "); + expressionPrinter.Visit(Lambda(PendingSelector, CurrentParameter)); + expressionPrinter.StringBuilder.AppendLine(); + if (CardinalityReducingGenericMethodInfo != null) + { + expressionPrinter.StringBuilder.AppendLine("CardinalityReducingMethod: " + CardinalityReducingGenericMethodInfo.Name); + } + } + } + public override ExpressionType NodeType => ExpressionType.Extension; public override Type Type => CardinalityReducingGenericMethodInfo == null ? typeof(IQueryable<>).MakeGenericType(PendingSelector.Type) : PendingSelector.Type; } - public class NavigationTreeExpression : NavigationTreeNode + public class NavigationTreeExpression : NavigationTreeNode, IPrintable { public NavigationTreeExpression(Expression value) : base(null, null) @@ -102,9 +120,22 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return this; } public override Type Type => Value.Type; + + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.AppendLine(nameof(NavigationTreeExpression)); + using (expressionPrinter.Indent()) + { + expressionPrinter.Append("Value: "); + expressionPrinter.Visit(Value); + expressionPrinter.AppendLine(); + expressionPrinter.Append("Expression: "); + expressionPrinter.Visit(GetExpression()); + } + } } - public class EntityReference : Expression + public class EntityReference : Expression, IPrintable { public EntityReference(IEntityType entityType) { @@ -148,6 +179,22 @@ public virtual void MarkAsOptional() IsOptional = true; } + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.Append(nameof(EntityReference)); + expressionPrinter.Append(EntityType.DisplayName()); + if (IsOptional) + { + expressionPrinter.StringBuilder.Append("[Optional]"); + } + + if (IncludePaths.Count > 0) + { + // TODO: fully render nested structure of include tree + expressionPrinter.Append(" | IncludePaths: " + string.Join(" ", IncludePaths.Select(ip => ip.Value.Count() > 0 ? ip.Key.Name + "->..." : ip.Key.Name))); + } + } + public virtual bool IsOptional { get; private set; } public override ExpressionType NodeType => ExpressionType.Extension; diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 531252cd3db..7753479c4a7 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -2,26 +2,20 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Collections; using System.Collections.Generic; -using System.ComponentModel.DataAnnotations; -using System.Diagnostics; using System.Linq; using System.Linq.Expressions; using System.Reflection; -using System.Runtime.InteropServices.ComTypes; -using Microsoft.EntityFrameworkCore.ChangeTracking; using Microsoft.EntityFrameworkCore.Diagnostics; -using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; -using Microsoft.EntityFrameworkCore.Query.Internal; namespace Microsoft.EntityFrameworkCore.Query.Internal { public partial class NavigationExpandingExpressionVisitor : ExpressionVisitor { + private readonly QueryCompilationContext _queryCompilationContext; private readonly IModel _model; private readonly bool _isTracking; private readonly PendingSelectorExpandingExpressionVisitor _pendingSelectorExpandingExpressionVisitor; @@ -29,18 +23,33 @@ public partial class NavigationExpandingExpressionVisitor : ExpressionVisitor private readonly ReducingExpressionVisitor _reducingExpressionVisitor; private readonly EntityReferenceOptionalMarkingExpressionVisitor _entityReferenceOptionalMarkingExpressionVisitor; private readonly ISet _parameterNames = new HashSet(); + private readonly ParameterExtractingExpressionVisitor _parameterExtractingExpressionVisitor; + private readonly Dictionary _parameterizedQueryFilterPredicateCache + = new Dictionary(); + private readonly Parameters _parameters = new Parameters(); + private static readonly MethodInfo _enumerableToListMethodInfo = typeof(Enumerable).GetTypeInfo() .GetDeclaredMethods(nameof(Enumerable.ToList)) .Single(mi => mi.GetParameters().Length == 1); - public NavigationExpandingExpressionVisitor(QueryCompilationContext queryCompilationContext) + public NavigationExpandingExpressionVisitor( + QueryCompilationContext queryCompilationContext, + IEvaluatableExpressionFilter evaluatableExpressionFilter) { + _queryCompilationContext = queryCompilationContext; _model = queryCompilationContext.Model; _isTracking = queryCompilationContext.IsTracking; _pendingSelectorExpandingExpressionVisitor = new PendingSelectorExpandingExpressionVisitor(this); _subqueryMemberPushdownExpressionVisitor = new SubqueryMemberPushdownExpressionVisitor(); _reducingExpressionVisitor = new ReducingExpressionVisitor(); _entityReferenceOptionalMarkingExpressionVisitor = new EntityReferenceOptionalMarkingExpressionVisitor(); + _parameterExtractingExpressionVisitor = new ParameterExtractingExpressionVisitor( + evaluatableExpressionFilter, + _parameters, + _queryCompilationContext.ContextType, + _queryCompilationContext.Logger, + parameterize: false, + generateContextAccessors: true); } private string GetParameterName(string prefix) @@ -61,10 +70,41 @@ public virtual Expression Expand(Expression query) var result = Visit(query); result = _pendingSelectorExpandingExpressionVisitor.Visit(result); result = new IncludeApplyingExpressionVisitor(this, _isTracking).Visit(result); + result = Reduce(result); + + var dbContextOnQueryContextPropertyAccess = + Expression.Convert( + Expression.Property( + QueryCompilationContext.QueryContextParameter, + _queryContextContextPropertyInfo), + _queryCompilationContext.ContextType); + + foreach (var parameterValue in _parameters.ParameterValues) + { + var lambda = (LambdaExpression)parameterValue.Value; + var remappedLambdaBody = ReplacingExpressionVisitor.Replace( + lambda.Parameters[0], + dbContextOnQueryContextPropertyAccess, + lambda.Body); + + _queryCompilationContext.RegisterRuntimeParameter( + parameterValue.Key, + Expression.Lambda( + remappedLambdaBody.Type.IsValueType + ? Expression.Convert(remappedLambdaBody, typeof(object)) + : remappedLambdaBody, + QueryCompilationContext.QueryContextParameter), + remappedLambdaBody.Type); + } - return Reduce(result); + return result; } + private static readonly PropertyInfo _queryContextContextPropertyInfo + = typeof(QueryContext) + .GetTypeInfo() + .GetDeclaredProperty(nameof(QueryContext.Context)); + protected override Expression VisitConstant(ConstantExpression constantExpression) { if (constantExpression.IsEntityQueryable()) @@ -75,8 +115,42 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio var currentTree = new NavigationTreeExpression(entityReference); var parameterName = GetParameterName(entityType.ShortName()[0].ToString().ToLower()); + var result = (Expression)new NavigationExpansionExpression(constantExpression, currentTree, currentTree, parameterName); - return new NavigationExpansionExpression(constantExpression, currentTree, currentTree, parameterName); + if (!_queryCompilationContext.IgnoreQueryFilters) + { + var rootEntityType = entityType.RootType(); + var queryFilterAnnotation = rootEntityType.FindAnnotation("QueryFilter"); + if (queryFilterAnnotation != null) + { + if (!_parameterizedQueryFilterPredicateCache.TryGetValue(rootEntityType, out var filterPredicate)) + { + filterPredicate = (LambdaExpression)queryFilterAnnotation.Value; + filterPredicate = (LambdaExpression)_parameterExtractingExpressionVisitor.ExtractParameters(filterPredicate); + _parameterizedQueryFilterPredicateCache[rootEntityType] = filterPredicate; + } + + 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); + var newFilterPredicateBody = ReplacingExpressionVisitor.Replace(filterPredicateParameter, newFilterPredicateParameter, filterPredicate.Body); + filterPredicate = Expression.Lambda(newFilterPredicateBody, newFilterPredicateParameter); + } + + var filteredResult = (Expression)Expression.Call( + QueryableMethodProvider.WhereMethodInfo.MakeGenericMethod(sequenceType), + result, + filterPredicate); + + result = Visit(filteredResult); + } + } + + return result; } return base.VisitConstant(constantExpression); @@ -1259,5 +1333,30 @@ private Expression ExpandNavigationsInLambdaExpression( return ExpandNavigationsInExpression(source, lambdaBody); } + + private class Parameters : IParameterValues + { + private readonly IDictionary _parameterValues = new Dictionary(); + + public IReadOnlyDictionary ParameterValues => (IReadOnlyDictionary)_parameterValues; + + public virtual void Add(string name, object value) + { + _parameterValues.Add(name, value); + } + + public virtual void Replace(string name, object value) + { + _parameterValues[name] = value; + } + + public virtual object Remove(string name) + { + var value = _parameterValues[name]; + _parameterValues.Remove(name); + + return value; + } + } } } diff --git a/src/EFCore/Query/Internal/QueryMetadataExtractingExpressionVisitor.cs b/src/EFCore/Query/Internal/QueryMetadataExtractingExpressionVisitor.cs index f848954c937..626241c4d00 100644 --- a/src/EFCore/Query/Internal/QueryMetadataExtractingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/QueryMetadataExtractingExpressionVisitor.cs @@ -39,6 +39,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/QueryCompilationContext.cs b/src/EFCore/Query/QueryCompilationContext.cs index f8fbf892899..bf12ed27e41 100644 --- a/src/EFCore/Query/QueryCompilationContext.cs +++ b/src/EFCore/Query/QueryCompilationContext.cs @@ -51,6 +51,7 @@ public QueryCompilationContext( public virtual IModel Model { get; } public virtual IDbContextOptions ContextOptions { get; } public virtual bool IsTracking { get; internal set; } + public virtual bool IgnoreQueryFilters { get; internal set; } public virtual ISet Tags { get; } = new HashSet(); public virtual IDiagnosticsLogger Logger { get; } public virtual Type ContextType { get; } diff --git a/src/EFCore/Query/QueryOptimizer.cs b/src/EFCore/Query/QueryOptimizer.cs index dbe7953294f..42434291604 100644 --- a/src/EFCore/Query/QueryOptimizer.cs +++ b/src/EFCore/Query/QueryOptimizer.cs @@ -31,7 +31,7 @@ public virtual Expression Visit(Expression query) query = new NullCheckRemovingExpressionVisitor().Visit(query); query = new EntityEqualityRewritingExpressionVisitor(_queryCompilationContext).Rewrite(query); query = new SubqueryMemberPushdownExpressionVisitor().Visit(query); - query = new NavigationExpandingExpressionVisitor(_queryCompilationContext).Expand(query); + query = new NavigationExpandingExpressionVisitor(_queryCompilationContext, Dependencies.EvaluatableExpressionFilter).Expand(query); query = new FunctionPreprocessingVisitor().Visit(query); new EnumerableVerifyingExpressionVisitor().Visit(query); diff --git a/src/EFCore/Query/QueryOptimizerDependencies.cs b/src/EFCore/Query/QueryOptimizerDependencies.cs index 2ef40ccfcce..bfaeed1f802 100644 --- a/src/EFCore/Query/QueryOptimizerDependencies.cs +++ b/src/EFCore/Query/QueryOptimizerDependencies.cs @@ -1,7 +1,10 @@ // 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.Diagnostics.CodeAnalysis; using Microsoft.EntityFrameworkCore.Infrastructure; +using Microsoft.EntityFrameworkCore.Query.Internal; +using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; namespace Microsoft.EntityFrameworkCore.Query @@ -51,8 +54,25 @@ public sealed class QueryOptimizerDependencies /// /// [EntityFrameworkInternal] - public QueryOptimizerDependencies() + public QueryOptimizerDependencies( + [NotNull] IEvaluatableExpressionFilter evaluatableExpressionFilter) { + Check.NotNull(evaluatableExpressionFilter, nameof(evaluatableExpressionFilter)); + + EvaluatableExpressionFilter = evaluatableExpressionFilter; } + + /// + /// Evaluatable expression filter. + /// + public IEvaluatableExpressionFilter EvaluatableExpressionFilter { get; } + + /// + /// Clones this dependency parameter object with one service replaced. + /// + /// A replacement for the current dependency of this type. + /// A new parameter object with the given service replaced. + public QueryOptimizerDependencies With([NotNull] IEvaluatableExpressionFilter evaluatableExpressionFilter) + => new QueryOptimizerDependencies(evaluatableExpressionFilter); } } diff --git a/src/EFCore/Query/ReplacingExpressionVisitor.cs b/src/EFCore/Query/ReplacingExpressionVisitor.cs index e01de68b2ba..a62a9f75fb1 100644 --- a/src/EFCore/Query/ReplacingExpressionVisitor.cs +++ b/src/EFCore/Query/ReplacingExpressionVisitor.cs @@ -89,5 +89,4 @@ protected override Expression VisitMethodCall(MethodCallExpression methodCallExp return base.VisitMethodCall(methodCallExpression); } } - } diff --git a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs index 110bc449f42..b7c3d8e4af4 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -21,9 +21,8 @@ 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 + typeof(FiltersInheritanceTestBase<>), // issue #17047 // Query pipeline typeof(SimpleQueryTestBase<>), typeof(GroupByQueryTestBase<>), diff --git a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs index 2fed8bd7aec..59a93b9250b 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInMemoryTest.cs @@ -1,17 +1,35 @@ // 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 Xunit; using Xunit.Abstractions; namespace Microsoft.EntityFrameworkCore.Query { - //issue #15264 - internal class FiltersInMemoryTest : FiltersTestBase> + public class FiltersInMemoryTest : FiltersTestBase> { public FiltersInMemoryTest(NorthwindQueryInMemoryFixture fixture, ITestOutputHelper testOutputHelper) : base(fixture) { //TestLoggerFactory.TestOutputHelper = testOutputHelper; } + + [ConditionalFact(Skip = "issue #16963")] + public override void Include_query() + { + base.Include_query(); + } + + [ConditionalFact(Skip = "issue #16963")] + public override void Include_query_opt_out() + { + base.Include_query_opt_out(); + } + + [ConditionalFact(Skip = "issue #16963")] + public override void Included_many_to_one_query() + { + base.Included_many_to_one_query(); + } } } diff --git a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs index 12dd0ad5cb7..3a467f043d8 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs @@ -3,7 +3,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 + // issue #17047 internal class FiltersInheritanceInMemoryTest : FiltersInheritanceTestBase { public FiltersInheritanceInMemoryTest(FiltersInheritanceInMemoryFixture fixture) diff --git a/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs b/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs index 738e32be31d..72624b5c896 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/SimpleQueryInMemoryTest.cs @@ -264,5 +264,17 @@ public override Task Project_single_element_from_collection_with_OrderBy_Distinc { return base.Project_single_element_from_collection_with_OrderBy_Distinct_and_FirstOrDefault_followed_by_projecting_length(isAsync); } + + [ConditionalTheory(Skip = "Issue#16963")] + public override Task QueryType_with_included_nav(bool isAsync) + { + return base.QueryType_with_included_nav(isAsync); + } + + [ConditionalTheory(Skip = "Issue#16963")] + public override Task QueryType_with_included_navs_multi_level(bool isAsync) + { + return base.QueryType_with_included_navs_multi_level(isAsync); + } } } 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] 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..3782ae4f584 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,13 @@ 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.Throws(() => context.Set().ToList()); context.TenantIds = new List(); var query = context.Set().ToList(); @@ -101,7 +100,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 +124,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 +138,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 +148,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 +166,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 +183,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 +198,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 +213,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 +223,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 +247,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 +262,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 +277,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 +291,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 +306,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()) @@ -351,7 +350,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 +361,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 +372,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 +383,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 +396,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 +405,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..060fd125441 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) { @@ -79,9 +79,9 @@ public virtual Task QueryType_with_defining_query(bool isAsync) ovs => ovs.AsNoTracking().Where(ov => ov.CustomerID == "ALFKI")); } - // #issue 12873 - //[ConditionalTheory] - //[MemberData(nameof(IsAsyncData))] + // also issue 12873 + [ConditionalTheory(Skip = "issue #16323")] + [MemberData(nameof(IsAsyncData))] public virtual Task QueryType_with_defining_query_and_correlated_collection(bool isAsync) { return AssertQuery( @@ -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 #16293")] [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.Specification.Tests/TestUtilities/QueryTestGeneration/ProceduralQueryExpressionGenerator.cs b/test/EFCore.Specification.Tests/TestUtilities/QueryTestGeneration/ProceduralQueryExpressionGenerator.cs index 7468e7701f5..39b3ede927f 100644 --- a/test/EFCore.Specification.Tests/TestUtilities/QueryTestGeneration/ProceduralQueryExpressionGenerator.cs +++ b/test/EFCore.Specification.Tests/TestUtilities/QueryTestGeneration/ProceduralQueryExpressionGenerator.cs @@ -480,7 +480,7 @@ public void Execute(IQueryable query, DbContext context, str } // printed just for debugging purposes - var queryString = new ExpressionPrinter().Print(newExpression, printConnections: false); + var queryString = new ExpressionPrinter().Print(newExpression); try { diff --git a/test/EFCore.SqlServer.FunctionalTests/Query/FiltersInheritanceSqlServerTest.cs b/test/EFCore.SqlServer.FunctionalTests/Query/FiltersInheritanceSqlServerTest.cs index 42991cac641..c1a62b275a6 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,7 @@ 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')"); } 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 9d0f2ad8d26..352b598e359 100644 --- a/test/EFCore.SqlServer.FunctionalTests/Query/FiltersSqlServerTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/Query/FiltersSqlServerTest.cs @@ -2,15 +2,13 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System.Linq; -using Microsoft.EntityFrameworkCore.TestUtilities.Xunit; using Xunit; using Xunit.Abstractions; // 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 +26,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 +47,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 +60,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 +72,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 +84,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 +102,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 +114,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 +124,20 @@ 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], [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[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 ( + SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] + FROM [Orders] AS [o] + LEFT JOIN ( + SELECT [c0].[CustomerID], [c0].[Address], [c0].[City], [c0].[CompanyName], [c0].[ContactName], [c0].[ContactTitle], [c0].[Country], [c0].[Fax], [c0].[Phone], [c0].[PostalCode], [c0].[Region] + FROM [Customers] AS [c0] + WHERE ((@__ef_filter__TenantPrefix_0 = N'') AND @__ef_filter__TenantPrefix_0 IS NOT NULL) OR ([c0].[CompanyName] IS NOT NULL AND (@__ef_filter__TenantPrefix_0 IS NOT NULL AND (([c0].[CompanyName] LIKE [c0].[CompanyName] + N'%') AND (((LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) = @__ef_filter__TenantPrefix_0) AND (LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NOT NULL AND @__ef_filter__TenantPrefix_0 IS NOT NULL)) OR (LEFT([c0].[CompanyName], LEN(@__ef_filter__TenantPrefix_0)) IS NULL AND @__ef_filter__TenantPrefix_0 IS NULL))))) + ) AS [t] ON [o].[CustomerID] = [t].[CustomerID] + WHERE [t].[CompanyName] IS NOT NULL +) AS [t0] ON [c].[CustomerID] = [t0].[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], [t0].[OrderID]"); } public override void Include_query_opt_out() @@ -150,17 +145,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 +160,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 +234,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()) @@ -235,13 +245,22 @@ public void FromSql_is_composed() } 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] -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'')"); + + [ConditionalFact(Skip = "issue #16326")] + public void FromSql_is_composed_when_filter_has_navigation() + { + using (var context = CreateContext()) + { + var results = context.Orders.FromSqlRaw("select * from Orders").ToList(); + + Assert.Equal(9999, results.Count); + } + + AssertSql( + @""); } public override void Compiled_query() @@ -254,14 +273,14 @@ public override void Compiled_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'')) AND ([c].[CustomerID] = @__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)))))) AND (([c].[CustomerID] = @__customerID) AND @__customerID IS NOT NULL)", // @"@__ef_filter__TenantPrefix_0='B' (Size = 4000) @__customerID='BLAUS' (Size = 5) 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'')) AND ([c].[CustomerID] = @__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)))))) AND (([c].[CustomerID] = @__customerID) AND @__customerID IS NOT NULL)"); } private void AssertSql(params string[] expected) 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 eba51dccd36..c965544dda9 100644 --- a/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs +++ b/test/EFCore.SqlServer.FunctionalTests/SqlServerComplianceTest.cs @@ -12,9 +12,6 @@ public class SqlServerComplianceTest : RelationalComplianceTestBase { protected override ICollection IgnoredTestBases { get; } = new HashSet { - typeof(FiltersInheritanceTestBase<>), // issue #15264 - typeof(FiltersTestBase<>), // issue #15264 - typeof(QueryFilterFuncletizationTestBase<>), // issue #15264 // Query pipeline typeof(ConcurrencyDetectorTestBase<>), typeof(ConcurrencyDetectorRelationalTestBase<>), 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..f7b1a25ab71 100644 --- a/test/EFCore.Sqlite.FunctionalTests/Query/QueryFilterFuncletizationSqliteTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/Query/QueryFilterFuncletizationSqliteTest.cs @@ -6,9 +6,7 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 - internal class QueryFilterFuncletizationSqliteTest - : QueryFilterFuncletizationTestBase + public class QueryFilterFuncletizationSqliteTest : QueryFilterFuncletizationTestBase { public QueryFilterFuncletizationSqliteTest( QueryFilterFuncletizationSqliteFixture fixture, ITestOutputHelper testOutputHelper) diff --git a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs index aa06d57aa9c..dd917582776 100644 --- a/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs +++ b/test/EFCore.Sqlite.FunctionalTests/SqliteComplianceTest.cs @@ -15,9 +15,6 @@ public class SqliteComplianceTest : RelationalComplianceTestBase typeof(FromSqlSprocQueryTestBase<>), typeof(SqlExecutorTestBase<>), typeof(UdfDbFunctionTestBase<>), - typeof(FiltersInheritanceTestBase<>), // issue #15264 - typeof(FiltersTestBase<>), // issue #15264 - typeof(QueryFilterFuncletizationTestBase<>), // issue #15264 // Query pipeline typeof(ConcurrencyDetectorTestBase<>), typeof(ConcurrencyDetectorRelationalTestBase<>),