From c058f5b847eaf4d9557062b2100713e23adad839 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. --- src/EFCore/Internal/EntityFinder.cs | 2 +- src/EFCore/Query/ExpressionPrinter.cs | 26 ++- ...ingExpressionVisitor.ExpressionVisitors.cs | 25 +-- ...nExpandingExpressionVisitor.Expressions.cs | 59 ++++- .../NavigationExpandingExpressionVisitor.cs | 70 +++++- ...ueryMetadataExtractingExpressionVisitor.cs | 9 + src/EFCore/Query/QueryCompilationContext.cs | 89 ++++++++ .../QueryCompilationContextDependencies.cs | 52 ++++- .../Query/ReplacingExpressionVisitor.cs | 1 - .../InMemoryComplianceTest.cs | 3 +- .../Query/FiltersInMemoryTest.cs | 22 +- .../Query/FiltersInheritanceInMemoryTest.cs | 1 - .../Query/SimpleQueryInMemoryTest.cs | 12 + .../Query/FiltersInheritanceTestBase.cs | 22 +- .../Query/FiltersTestBase.cs | 40 ++-- .../QueryFilterFuncletizationTestBase.cs | 52 ++--- .../Query/SimpleQueryTestBase.QueryTypes.cs | 20 +- .../Query/FiltersInheritanceSqlServerTest.cs | 25 +-- .../Query/FiltersSqlServerTest.cs | 113 +++++----- .../QueryFilterFuncletizationSqlServerTest.cs | 206 +++++++++--------- .../SqlServerComplianceTest.cs | 3 - .../Query/FiltersInheritanceSqliteTest.cs | 3 +- .../Query/FiltersSqliteTest.cs | 6 +- .../QueryFilterFuncletizationSqliteTest.cs | 4 +- .../SqliteComplianceTest.cs | 3 - 25 files changed, 582 insertions(+), 286 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/Query/ExpressionPrinter.cs b/src/EFCore/Query/ExpressionPrinter.cs index 60868658ba6..30f6a32963a 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; @@ -474,7 +476,17 @@ protected override Expression VisitLambda(Expression lambdaExpression) _parametersInScope.Add(parameter, parameterName); } - _stringBuilder.Append(parameter.Type.ShortDisplayName() + " " + parameterName); + var parameterIndex = _encounteredParameters.Count; + if (_encounteredParameters.Contains(parameter)) + { + parameterIndex = _encounteredParameters.IndexOf(parameter); + } + else + { + _encounteredParameters.Add(parameter); + } + + _stringBuilder.Append(parameter.Type.ShortDisplayName() + " " + parameterName + "{" + parameterIndex + "}"); if (parameter != lambdaExpression.Parameters.Last()) { @@ -779,6 +791,18 @@ protected override Expression VisitParameter(ParameterExpression parameterExpres Append(")"); } + var parameterIndex = _encounteredParameters.Count; + if (_encounteredParameters.Contains(parameterExpression)) + { + parameterIndex = _encounteredParameters.IndexOf(parameterExpression); + } + else + { + _encounteredParameters.Add(parameterExpression); + } + + _stringBuilder.Append("{" + parameterIndex + "}"); + return parameterExpression; } diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs index 956a0b8089d..4b8de4997b7 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.ExpressionVisitors.cs @@ -170,7 +170,7 @@ protected Expression ExpandNavigation( innerEntityReference.SetIncludePaths(innerIncludeTreeNode); } - var innerParameter = Expression.Parameter(innerSource.SourceElementType, "i"); + var innerParameter = Expression.Parameter(innerSource.Type.GetSequenceType(), "i"); Expression outerKey; if (root is NavigationExpansionExpression innerNavigationExpansionExpression && innerNavigationExpansionExpression.CardinalityReducingGenericMethodInfo != null) @@ -196,7 +196,7 @@ protected Expression ExpandNavigation( : navigation.ForeignKey.PrincipalKey.Properties); } - var innerKey = CreateKeyAccessExpression(innerParameter, + var innerKey = CreateKeyAccessExpression(innerSource.PendingSelector, navigation.IsDependentToPrincipal() ? navigation.ForeignKey.PrincipalKey.Properties : navigation.ForeignKey.Properties); @@ -218,15 +218,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(innerSource.Type.GetSequenceType()), + innerSource, + Expression.Quote( + Expression.Lambda( + Expression.Equal(outerKey, innerKey), innerParameter))); return new MaterializeCollectionNavigationExpression(subquery, navigation); } @@ -241,8 +238,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 +266,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..0f5bc670075 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,26 @@ public virtual void ConvertToSingleResult(MethodInfo genericMethod) protected override Expression VisitChildren(ExpressionVisitor visitor) => this; + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.Visit(Source); + expressionPrinter.StringBuilder.Append(".PendingSelector("); + expressionPrinter.Visit(PendingSelector); + expressionPrinter.StringBuilder.Append(")"); + + if (CardinalityReducingGenericMethodInfo != null) + { + expressionPrinter.StringBuilder.Append("." + 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 +115,27 @@ protected override Expression VisitChildren(ExpressionVisitor visitor) return this; } public override Type Type => Value.Type; + + public override void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.Append("NAVIGATION_TREE_EXPRESSION("); + + var parent = Parent; + var currentParameter = CurrentParameter; + while (currentParameter == null) + { + currentParameter = parent.CurrentParameter; + parent = parent.Parent; + } + + expressionPrinter.Visit(currentParameter); + expressionPrinter.Append(" | "); + expressionPrinter.Visit(Value); + expressionPrinter.Append(")"); + } } - public class EntityReference : Expression + public class EntityReference : Expression, IPrintable { public EntityReference(IEntityType entityType) { @@ -148,13 +179,26 @@ public virtual void MarkAsOptional() IsOptional = true; } + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.Append("ENTITY_REFERENCE("); + expressionPrinter.Append(EntityType.DisplayName()); + + if (IncludePaths.Count > 0) + { + expressionPrinter.Append(" | IncludePaths: " + string.Join(".", IncludePaths.Select(ip => ip.Key.Name))); + } + + expressionPrinter.Append(")"); + } + public virtual bool IsOptional { get; private set; } public override ExpressionType NodeType => ExpressionType.Extension; public override Type Type => EntityType.ClrType; } - public class NavigationTreeNode : Expression + public class NavigationTreeNode : Expression, IPrintable { private NavigationTreeNode _parent; @@ -203,6 +247,13 @@ public virtual Expression GetExpression() ? MakeMemberAccess(parentExperssion, parentExperssion.Type.GetTypeInfo().GetMember("Outer")[0]) : MakeMemberAccess(parentExperssion, parentExperssion.Type.GetTypeInfo().GetMember("Inner")[0]); } + + public virtual void Print(ExpressionPrinter expressionPrinter) + { + expressionPrinter.Append("NAVIGATION_TREE_NODE("); + expressionPrinter.Visit(CurrentParameter); + expressionPrinter.Append(")"); + } } public class OwnedNavigationReference : Expression diff --git a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs index 893520242b8..f7428a11aef 100644 --- a/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs +++ b/src/EFCore/Query/Internal/NavigationExpandingExpressionVisitor.cs @@ -2,38 +2,35 @@ // 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; private readonly SubqueryMemberPushdownExpressionVisitor _subqueryMemberPushdownExpressionVisitor; private readonly ReducingExpressionVisitor _reducingExpressionVisitor; private readonly ISet _parameterNames = new HashSet(); + private readonly List _appliedQueryFilters = new List(); + private static readonly MethodInfo _enumerableToListMethodInfo = typeof(Enumerable).GetTypeInfo() .GetDeclaredMethods(nameof(Enumerable.ToList)) .Single(mi => mi.GetParameters().Length == 1); public NavigationExpandingExpressionVisitor(QueryCompilationContext queryCompilationContext) { + _queryCompilationContext = queryCompilationContext; _model = queryCompilationContext.Model; _isTracking = queryCompilationContext.IsTracking; _pendingSelectorExpandingExpressionVisitor = new PendingSelectorExpandingExpressionVisitor(this); @@ -74,7 +71,44 @@ protected override Expression VisitConstant(ConstantExpression constantExpressio var currentTree = new NavigationTreeExpression(entityReference); var parameterName = GetParameterName(entityType.ShortName()[0].ToString().ToLower()); - return new NavigationExpansionExpression(constantExpression, currentTree, currentTree, parameterName); + var result = (Expression)new NavigationExpansionExpression(constantExpression, currentTree, currentTree, parameterName); + + var rootEntityType = entityType.RootType(); + var queryFilterAnnotation = rootEntityType.FindAnnotation("QueryFilter"); + if (queryFilterAnnotation != null + && !_queryCompilationContext.IgnoreQueryFilters + && !_appliedQueryFilters.Contains(rootEntityType)) + { + _appliedQueryFilters.Add(rootEntityType); + var filterPredicate = (LambdaExpression)queryFilterAnnotation.Value; + + var parameterExtractingExpressionVisitor = new ParameterExtractingExpressionVisitor( + _queryCompilationContext.EvaluatableExpressionFilter, + _queryCompilationContext.ParameterValues, + _queryCompilationContext.ContextType, + _queryCompilationContext.Logger, + parameterize: false, + generateContextAccessors: true); + + filterPredicate = (LambdaExpression)parameterExtractingExpressionVisitor.ExtractParameters(filterPredicate); + 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 whereMethod = QueryableMethodProvider.WhereMethodInfo.MakeGenericMethod(result.Type.GetSequenceType()); + var filteredResult = (Expression)Expression.Call(whereMethod, result, filterPredicate); + + result = Visit(filteredResult); + } + + return result; } return base.VisitConstant(constantExpression); @@ -1180,11 +1214,31 @@ private Expression ExpandNavigationsInLambdaExpression( NavigationExpansionExpression source, LambdaExpression lambdaExpression) { + + var sikson = new Microsoft.EntityFrameworkCore.Query.ExpressionPrinter().Print(lambdaExpression.Body); + var lambdaBody = ReplacingExpressionVisitor.Replace( lambdaExpression.Parameters[0], source.PendingSelector, lambdaExpression.Body); + + var kupson = new Microsoft.EntityFrameworkCore.Query.ExpressionPrinter().Print(lambdaBody); + + return ExpandNavigationsInExpression(source, lambdaBody); + } + + + private Expression ExpandNavigationsInLambdaExpression( + NavigationExpansionExpression source, + LambdaExpression lambdaExpression, + Expression expressionToReplace) + { + var lambdaBody = ReplacingExpressionVisitor.Replace( + expressionToReplace, + source.PendingSelector, + lambdaExpression.Body); + return ExpandNavigationsInExpression(source, lambdaBody); } } 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..14f3c7519d1 100644 --- a/src/EFCore/Query/QueryCompilationContext.cs +++ b/src/EFCore/Query/QueryCompilationContext.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Query.Internal; namespace Microsoft.EntityFrameworkCore.Query { @@ -28,6 +29,8 @@ public class QueryCompilationContext /// private Dictionary _runtimeParameters; + private readonly Parameters _parameters; + public QueryCompilationContext( QueryCompilationContextDependencies dependencies, bool async) @@ -40,21 +43,27 @@ public QueryCompilationContext( ContextOptions = dependencies.ContextOptions; ContextType = context.GetType(); Logger = dependencies.Logger; + EvaluatableExpressionFilter = dependencies.EvaluatableExpressionFilter; _queryOptimizerFactory = dependencies.QueryOptimizerFactory; _queryableMethodTranslatingExpressionVisitorFactory = dependencies.QueryableMethodTranslatingExpressionVisitorFactory; _shapedQueryOptimizerFactory = dependencies.ShapedQueryOptimizerFactory; _shapedQueryCompilingExpressionVisitorFactory = dependencies.ShapedQueryCompilingExpressionVisitorFactory; + _parameters = new Parameters(); } public virtual bool IsAsync { get; } 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 IEvaluatableExpressionFilter EvaluatableExpressionFilter { get; } public virtual Type ContextType { get; } + internal virtual IParameterValues ParameterValues => _parameters; + public virtual void AddTag(string tag) { Tags.Add(tag); @@ -71,6 +80,16 @@ public virtual Func CreateQueryExecutor(Expressi // Inject tracking query = _shapedQueryCompilingExpressionVisitorFactory.Create(this).Visit(query); + var setFilterParameterExpressions + = CreateSetFilterParametersExpressions(out var contextVariableExpression); + + if (setFilterParameterExpressions != null) + { + query = Expression.Block( + new[] { contextVariableExpression }, + setFilterParameterExpressions.Concat(new[] { query })); + } + // If any additional parameters were added during the compilation phase (e.g. entity equality ID expression), // wrap the query with code adding those parameters to the query context query = InsertRuntimeParameters(query); @@ -127,5 +146,75 @@ private static readonly MethodInfo _queryContextAddParameterMethodInfo = typeof(QueryContext) .GetTypeInfo() .GetDeclaredMethod(nameof(QueryContext.Add)); + + private static readonly PropertyInfo _queryContextContextPropertyInfo + = typeof(QueryContext) + .GetTypeInfo() + .GetDeclaredProperty(nameof(QueryContext.Context)); + + private IEnumerable CreateSetFilterParametersExpressions(out ParameterExpression contextVariableExpression) + { + contextVariableExpression = null; + + if (_parameters.ParameterValues.Count == 0) + { + return null; + } + + contextVariableExpression = Expression.Variable(ContextType, "context"); + + var blockExpressions + = new List + { + Expression.Assign( + contextVariableExpression, + Expression.Convert( + Expression.Property( + QueryContextParameter, + _queryContextContextPropertyInfo), + ContextType)) + }; + + foreach (var keyValuePair in _parameters.ParameterValues) + { + blockExpressions.Add( + Expression.Call( + QueryContextParameter, + _queryContextAddParameterMethodInfo, + Expression.Constant(keyValuePair.Key), + Expression.Convert( + Expression.Invoke( + (LambdaExpression)keyValuePair.Value, + contextVariableExpression), + typeof(object)))); + } + + return blockExpressions; + } + + private class Parameters : IParameterValues + { + private readonly IDictionary _parameterValues = new Dictionary(); + + public IReadOnlyDictionary ParameterValues => (IReadOnlyDictionary)_parameterValues; + + public virtual void 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/QueryCompilationContextDependencies.cs b/src/EFCore/Query/QueryCompilationContextDependencies.cs index 971a2baf949..94b3aec1c9a 100644 --- a/src/EFCore/Query/QueryCompilationContextDependencies.cs +++ b/src/EFCore/Query/QueryCompilationContextDependencies.cs @@ -5,6 +5,7 @@ using Microsoft.EntityFrameworkCore.Diagnostics; using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; +using Microsoft.EntityFrameworkCore.Query.Internal; using Microsoft.EntityFrameworkCore.Utilities; using Microsoft.Extensions.DependencyInjection; @@ -63,7 +64,8 @@ public QueryCompilationContextDependencies( [NotNull] IShapedQueryCompilingExpressionVisitorFactory shapedQueryCompilingExpressionVisitorFactory, [NotNull] ICurrentDbContext currentContext, [NotNull] IDbContextOptions contextOptions, - [NotNull] IDiagnosticsLogger logger) + [NotNull] IDiagnosticsLogger logger, + [NotNull] IEvaluatableExpressionFilter evaluatableExpressionFilter) { Check.NotNull(model, nameof(model)); Check.NotNull(queryOptimizerFactory, nameof(queryOptimizerFactory)); @@ -73,6 +75,7 @@ public QueryCompilationContextDependencies( Check.NotNull(currentContext, nameof(currentContext)); Check.NotNull(contextOptions, nameof(contextOptions)); Check.NotNull(logger, nameof(logger)); + Check.NotNull(evaluatableExpressionFilter, nameof(evaluatableExpressionFilter)); CurrentContext = currentContext; Model = model; @@ -82,6 +85,7 @@ public QueryCompilationContextDependencies( ShapedQueryCompilingExpressionVisitorFactory = shapedQueryCompilingExpressionVisitorFactory; ContextOptions = contextOptions; Logger = logger; + EvaluatableExpressionFilter = evaluatableExpressionFilter; } /// @@ -124,6 +128,11 @@ public QueryCompilationContextDependencies( /// public IDiagnosticsLogger Logger { get; } + /// + /// Evaluatable expression filter. + /// + public IEvaluatableExpressionFilter EvaluatableExpressionFilter { get; } + /// /// Clones this dependency parameter object with one service replaced. /// @@ -138,7 +147,8 @@ public QueryCompilationContextDependencies With([NotNull] IModel model) ShapedQueryCompilingExpressionVisitorFactory, CurrentContext, ContextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -154,7 +164,8 @@ public QueryCompilationContextDependencies With([NotNull] IQueryOptimizerFactory ShapedQueryCompilingExpressionVisitorFactory, CurrentContext, ContextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -171,7 +182,8 @@ public QueryCompilationContextDependencies With( ShapedQueryCompilingExpressionVisitorFactory, CurrentContext, ContextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -187,7 +199,8 @@ public QueryCompilationContextDependencies With([NotNull] IShapedQueryOptimizerF ShapedQueryCompilingExpressionVisitorFactory, CurrentContext, ContextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -204,7 +217,8 @@ public QueryCompilationContextDependencies With( shapedQueryCompilingExpressionVisitorFactory, CurrentContext, ContextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -220,7 +234,8 @@ public QueryCompilationContextDependencies With([NotNull] ICurrentDbContext curr ShapedQueryCompilingExpressionVisitorFactory, currentContext, ContextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -236,7 +251,8 @@ public QueryCompilationContextDependencies With([NotNull] IDbContextOptions cont ShapedQueryCompilingExpressionVisitorFactory, CurrentContext, contextOptions, - Logger); + Logger, + EvaluatableExpressionFilter); /// /// Clones this dependency parameter object with one service replaced. @@ -252,6 +268,24 @@ public QueryCompilationContextDependencies With([NotNull] IDiagnosticsLogger + /// 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 QueryCompilationContextDependencies With([NotNull] IEvaluatableExpressionFilter evaluatableExpressionFilter) + => new QueryCompilationContextDependencies( + Model, + QueryOptimizerFactory, + QueryableMethodTranslatingExpressionVisitorFactory, + ShapedQueryOptimizerFactory, + ShapedQueryCompilingExpressionVisitorFactory, + CurrentContext, + ContextOptions, + Logger, + 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..062b0a3d456 100644 --- a/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/InMemoryComplianceTest.cs @@ -21,10 +21,9 @@ public class InMemoryComplianceTest : ComplianceTestBase typeof(GraphUpdatesTestBase<>), // issue #15318 typeof(ProxyGraphUpdatesTestBase<>), // issue #15318 typeof(ComplexNavigationsWeakQueryTestBase<>), // issue #15285 - typeof(FiltersInheritanceTestBase<>), // issue #15264 - typeof(FiltersTestBase<>), // issue #15264 typeof(OwnedQueryTestBase<>), // issue #15285 // Query pipeline + typeof(FiltersInheritanceTestBase<>), typeof(SimpleQueryTestBase<>), typeof(GroupByQueryTestBase<>), typeof(ConcurrencyDetectorTestBase<>), 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..5a3ae0a1c50 100644 --- a/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs +++ b/test/EFCore.InMemory.FunctionalTests/Query/FiltersInheritanceInMemoryTest.cs @@ -3,7 +3,6 @@ namespace Microsoft.EntityFrameworkCore.Query { - // issue #15264 internal class FiltersInheritanceInMemoryTest : FiltersInheritanceTestBase { public FiltersInheritanceInMemoryTest(FiltersInheritanceInMemoryFixture fixture) diff --git a/test/EFCore.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..be9ba780829 100644 --- a/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs +++ b/test/EFCore.Specification.Tests/Query/QueryFilterFuncletizationTestBase.cs @@ -21,7 +21,7 @@ public abstract class QueryFilterFuncletizationTestBase : IClassFixtur protected QueryFilterFuncletizationContext CreateContext() => Fixture.CreateContext(); - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_parameter_does_not_clash_with_closure_parameter_name() { using (var context = CreateContext()) @@ -31,7 +31,7 @@ public virtual void DbContext_property_parameter_does_not_clash_with_closure_par } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -46,7 +46,7 @@ public virtual void DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_is_parameterized() { using (var context = CreateContext()) @@ -61,7 +61,7 @@ public virtual void DbContext_property_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_method_call_is_parameterized() { using (var context = CreateContext()) @@ -71,14 +71,14 @@ public virtual void DbContext_method_call_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_list_is_parameterized() { using (var context = CreateContext()) { // This throws because the default value of TenantIds is null which is NRE var exception = Record.Exception(() => context.Set().ToList()); - Assert.True(exception is InvalidOperationException || exception is ArgumentNullException); + Assert.True(exception is InvalidOperationException || exception is ArgumentNullException || exception is NullReferenceException); context.TenantIds = new List(); var query = context.Set().ToList(); @@ -101,7 +101,7 @@ public virtual void DbContext_list_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_chain_is_parameterized() { using (var context = CreateContext()) @@ -125,7 +125,7 @@ public virtual void DbContext_property_chain_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_method_call_is_parameterized() { using (var context = CreateContext()) @@ -139,7 +139,7 @@ public virtual void DbContext_property_method_call_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_method_call_chain_is_parameterized() { using (var context = CreateContext()) @@ -149,7 +149,7 @@ public virtual void DbContext_method_call_chain_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_complex_expression_is_parameterized() { using (var context = CreateContext()) @@ -167,7 +167,7 @@ public virtual void DbContext_complex_expression_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void DbContext_property_based_filter_does_not_short_circuit() { using (var context = CreateContext()) @@ -184,7 +184,7 @@ public virtual void DbContext_property_based_filter_does_not_short_circuit() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -199,7 +199,7 @@ public virtual void EntityTypeConfiguration_DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_property_is_parameterized() { using (var context = CreateContext()) @@ -214,7 +214,7 @@ public virtual void EntityTypeConfiguration_DbContext_property_is_parameterized( } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_method_call_is_parameterized() { using (var context = CreateContext()) @@ -224,7 +224,7 @@ public virtual void EntityTypeConfiguration_DbContext_method_call_is_parameteriz } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void EntityTypeConfiguration_DbContext_property_chain_is_parameterized() { using (var context = CreateContext()) @@ -248,7 +248,7 @@ public virtual void EntityTypeConfiguration_DbContext_property_chain_is_paramete } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_method_DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -263,7 +263,7 @@ public virtual void Local_method_DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_static_method_DbContext_property_is_parameterized() { using (var context = CreateContext()) @@ -278,7 +278,7 @@ public virtual void Local_static_method_DbContext_property_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Remote_method_DbContext_property_method_call_is_parameterized() { using (var context = CreateContext()) @@ -292,7 +292,7 @@ public virtual void Remote_method_DbContext_property_method_call_is_parameterize } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Extension_method_DbContext_field_is_parameterized() { using (var context = CreateContext()) @@ -307,7 +307,7 @@ public virtual void Extension_method_DbContext_field_is_parameterized() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Extension_method_DbContext_property_chain_is_parameterized() { using (var context = CreateContext()) @@ -351,7 +351,7 @@ public virtual void Using_Context_set_method_in_filter_works() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Static_member_from_dbContext_is_inlined() { using (var context = CreateContext()) @@ -362,7 +362,7 @@ public virtual void Static_member_from_dbContext_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Static_member_from_non_dbContext_is_inlined() { using (var context = CreateContext()) @@ -373,7 +373,7 @@ public virtual void Static_member_from_non_dbContext_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_variable_from_OnModelCreating_is_inlined() { using (var context = CreateContext()) @@ -384,7 +384,7 @@ public virtual void Local_variable_from_OnModelCreating_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Local_variable_from_OnModelCreating_can_throw_exception() { using (var context = CreateContext()) @@ -397,7 +397,7 @@ public virtual void Local_variable_from_OnModelCreating_can_throw_exception() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Method_parameter_is_inlined() { using (var context = CreateContext()) @@ -406,7 +406,7 @@ public virtual void Method_parameter_is_inlined() } } - [ConditionalFact(Skip = "issue #15264")] + [ConditionalFact] public virtual void Using_multiple_context_in_filter_parametrize_only_current_context() { using (var context = CreateContext()) diff --git a/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.QueryTypes.cs b/test/EFCore.Specification.Tests/Query/SimpleQueryTestBase.QueryTypes.cs index b2e6057076b..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.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..63ce7f80cce 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,16 @@ 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], [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[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 [Customers] AS [c0] ON [o].[CustomerID] = [c0].[CustomerID] + WHERE [c0].[CompanyName] IS NOT NULL +) AS [t] ON [c].[CustomerID] = [t].[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], [t].[OrderID]"); } public override void Include_query_opt_out() @@ -150,17 +141,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 +156,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 +230,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 +241,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 +269,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<>),