From 757e6d4b1bcdad9e4831321edb9ecf42f4007fbd Mon Sep 17 00:00:00 2001 From: Andrew Peters Date: Wed, 14 Sep 2016 15:57:29 -0700 Subject: [PATCH] Query: Fix #4588 - Query :: SelectMany-GroupJoin-DefaultIfEmpty isn't being lifted into SQL for some complex queries, resulting in extensive client-side evaluation. Adds relational GroupJoin/DefaultIfEmpty elimination. --- .../SqlTranslatingExpressionVisitor.cs | 44 +++- .../Query/Expressions/SelectExpression.cs | 14 +- .../RelationalResultOperatorHandler.cs | 11 +- .../Query/RelationalQueryModelVisitor.cs | 146 ++++++++--- .../Query/Sql/DefaultQuerySqlGenerator.cs | 111 +++++---- .../QueryTestBase.cs | 22 +- .../Microsoft.EntityFrameworkCore.csproj | 1 + .../Query/EntityQueryModelVisitor.cs | 61 ++--- .../MemberAccessBindingExpressionVisitor.cs | 28 +++ ...equiresMaterializationExpressionVisitor.cs | 13 +- .../Query/Internal/QueryModelExtensions.cs | 62 +++++ .../Query/Internal/QueryOptimizer.cs | 45 ++-- .../Query/QueryCompilationContext.cs | 25 +- .../ComplexNavigationsQuerySqlServerTest.cs | 68 +++--- .../GearsOfWarQuerySqlServerTest.cs | 231 ++++++++---------- .../QueryNavigationsSqlServerTest.cs | 90 ++++--- .../QuerySqlServerTest.cs | 19 +- 17 files changed, 627 insertions(+), 364 deletions(-) create mode 100644 src/Microsoft.EntityFrameworkCore/Query/Internal/QueryModelExtensions.cs diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs index 4715f6cebb8..bdac9a69181 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs @@ -212,7 +212,8 @@ var structuralComparisonExpression var leftExpression = Visit(expression.Left); var rightExpression = Visit(expression.Right); - return leftExpression != null && rightExpression != null + return leftExpression != null + && rightExpression != null ? Expression.MakeBinary( expression.NodeType, leftExpression, @@ -252,6 +253,7 @@ protected override Expression VisitConditional(ConditionalExpression expression) var ifTrue = Visit(expression.IfTrue); var ifFalse = Visit(expression.IfFalse); + // ReSharper disable once ConditionIsAlwaysTrueOrFalse if (test != null && ifTrue != null && ifFalse != null) @@ -991,9 +993,21 @@ protected override Expression VisitExtension(Expression expression) var nullConditionalExpression = expression as NullConditionalExpression; - return nullConditionalExpression != null - ? Visit(nullConditionalExpression.AccessOperation) - : base.VisitExtension(expression); + if (nullConditionalExpression != null) + { + var newAccessOperation = Visit(nullConditionalExpression.AccessOperation); + + if (newAccessOperation != null + && newAccessOperation.Type != nullConditionalExpression.Type) + { + newAccessOperation + = Expression.Convert(newAccessOperation, nullConditionalExpression.Type); + } + + return newAccessOperation; + } + + return base.VisitExtension(expression); } /// @@ -1007,6 +1021,28 @@ protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpr { Check.NotNull(expression, nameof(expression)); + if (!_inProjection) + { + var joinClause + = expression.ReferencedQuerySource as JoinClause; + + if (joinClause != null) + { + var entityType + = _queryModelVisitor.QueryCompilationContext.Model + .FindEntityType(joinClause.ItemType); + + if (entityType != null) + { + return Visit( + EntityQueryModelVisitor.CreatePropertyExpression( + expression, entityType.FindPrimaryKey().Properties[0])); + } + + return null; + } + } + var selector = ((expression.ReferencedQuerySource as FromClauseBase) ?.FromExpression as SubQueryExpression) diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs index b204f6fe683..cab1034292c 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs @@ -476,14 +476,26 @@ public virtual int AddToProjection([NotNull] Expression expression) public virtual int AddToProjection([NotNull] Expression expression, bool resetProjectStar) { Check.NotNull(expression, nameof(expression)); + + if (expression.NodeType == ExpressionType.Convert) + { + var unaryExpression = (UnaryExpression)expression; + + if (unaryExpression.Type.UnwrapNullableType() + == unaryExpression.Operand.Type) + { + expression = unaryExpression.Operand; + } + } var columnExpression = expression as ColumnExpression; - var aliasExpression = expression as AliasExpression; if (columnExpression != null) { return AddToProjection(columnExpression); } + + var aliasExpression = expression as AliasExpression; if (aliasExpression != null) { diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs index 168c4cf2a72..25e56f2cce6 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/Internal/RelationalResultOperatorHandler.cs @@ -680,10 +680,11 @@ private static Expression HandleSkip(HandlerContext handlerContext) private static Expression HandleSum(HandlerContext handlerContext) { - if (!handlerContext.QueryModelVisitor.RequiresClientProjection) + if (!handlerContext.QueryModelVisitor.RequiresClientProjection + && handlerContext.SelectExpression.Projection.Count == 1) { var sumExpression - = new SumExpression(handlerContext.SelectExpression.Projection.Single()); + = new SumExpression(handlerContext.SelectExpression.Projection.First()); handlerContext.SelectExpression.SetProjectionExpression(sumExpression); @@ -699,12 +700,14 @@ private static Expression HandleTake(HandlerContext handlerContext) { var takeResultOperator = (TakeResultOperator)handlerContext.ResultOperator; - var sqlTranslatingExpressionVisitor = handlerContext.CreateSqlTranslatingVisitor(bindParentQueries: true); + var sqlTranslatingExpressionVisitor + = handlerContext.CreateSqlTranslatingVisitor(bindParentQueries: true); var limit = sqlTranslatingExpressionVisitor.Visit(takeResultOperator.Count); + if (limit != null) { - handlerContext.SelectExpression.Limit = takeResultOperator.Count; + handlerContext.SelectExpression.Limit = limit; return handlerContext.EvalOnServer; } diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs index 1528340eec1..fec99db3333 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs @@ -23,6 +23,8 @@ using Remotion.Linq; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; +using Remotion.Linq.Clauses.ExpressionVisitors; +using Remotion.Linq.Clauses.ResultOperators; namespace Microsoft.EntityFrameworkCore.Query { @@ -243,7 +245,7 @@ public virtual void RegisterSubQueryVisitor( Check.NotNull(querySource, nameof(querySource)); Check.NotNull(queryModelVisitor, nameof(queryModelVisitor)); - _subQueryModelVisitorsBySource.Add(querySource, queryModelVisitor); + _subQueryModelVisitorsBySource[querySource] = queryModelVisitor; } /// @@ -341,12 +343,13 @@ protected override void IncludeNavigations( Check.NotNull(includeSpecification, nameof(includeSpecification)); Check.NotNull(resultType, nameof(resultType)); - var includeExpressionVisitor = _includeExpressionVisitorFactory.Create( - includeSpecification.QuerySource, - includeSpecification.NavigationPath, - QueryCompilationContext, - _navigationIndexMap[includeSpecification], - querySourceRequiresTracking); + var includeExpressionVisitor + = _includeExpressionVisitorFactory.Create( + includeSpecification.QuerySource, + includeSpecification.NavigationPath, + QueryCompilationContext, + _navigationIndexMap[includeSpecification], + querySourceRequiresTracking); Expression = includeExpressionVisitor.Visit(Expression); } @@ -510,15 +513,12 @@ var fromQuerySourceReferenceExpression private bool CanFlattenSelectMany() { var selectManyExpression = Expression as MethodCallExpression; - if (selectManyExpression == null - || !selectManyExpression.Method.MethodIsClosedFormOf(LinqOperatorProvider.SelectMany) - || !IsShapedQueryExpression(selectManyExpression.Arguments[0] as MethodCallExpression, innerShapedQuery: false) - || !IsShapedQueryExpression((selectManyExpression.Arguments[1] as LambdaExpression)?.Body as MethodCallExpression, innerShapedQuery: true)) - { - return false; - } - return true; + return selectManyExpression != null + && selectManyExpression.Method.MethodIsClosedFormOf(LinqOperatorProvider.SelectMany) + && IsShapedQueryExpression(selectManyExpression.Arguments[0] as MethodCallExpression, innerShapedQuery: false) + && IsShapedQueryExpression((selectManyExpression.Arguments[1] as LambdaExpression) + ?.Body as MethodCallExpression, innerShapedQuery: true); } private bool IsShapedQueryExpression(MethodCallExpression shapedQueryExpression, bool innerShapedQuery) @@ -538,18 +538,14 @@ private bool IsShapedQueryExpression(MethodCallExpression shapedQueryExpression, } } - if (shapedQueryExpression == null || shapedQueryExpression.Arguments.Count != 3) + if (shapedQueryExpression.Arguments.Count != 3) { return false; } var shaper = shapedQueryExpression.Arguments[2] as ConstantExpression; - if (shaper == null || !(shaper.Value is Shaper)) - { - return false; - } - return true; + return shaper?.Value is Shaper; } /// @@ -627,7 +623,7 @@ public override void VisitGroupJoinClause( index, () => base.VisitGroupJoinClause(groupJoinClause, queryModel, index), LinqOperatorProvider.GroupJoin, - outerJoin: true); + groupJoin: true); } /// @@ -638,14 +634,14 @@ public override void VisitGroupJoinClause( /// Index of the node being visited. /// The base visit action. /// The operator to flatten. - /// true if an outer join should be performed. + /// true if an outer join should be performed. protected virtual void OptimizeJoinClause( [NotNull] JoinClause joinClause, [NotNull] QueryModel queryModel, int index, [NotNull] Action baseVisitAction, [NotNull] MethodInfo operatorToFlatten, - bool outerJoin = false) + bool groupJoin = false) { Check.NotNull(joinClause, nameof(joinClause)); Check.NotNull(queryModel, nameof(queryModel)); @@ -664,11 +660,14 @@ var previousSelectExpression var previousSelectProjectionCount = previousSelectExpression?.Projection.Count ?? -1; + var previousParameter = CurrentParameter; + var previousMapping = SnapshotQuerySourceMapping(queryModel); + baseVisitAction(); if (!RequiresClientSelectMany && previousSelectExpression != null - && (!operatorToFlatten.MethodIsClosedFormOf(LinqOperatorProvider.GroupJoin) + && (!groupJoin || CanFlattenGroupJoin())) { var selectExpression = TryGetQuery(joinClause); @@ -700,13 +699,13 @@ var projection : Enumerable.Empty(); var joinExpression - = !outerJoin + = !groupJoin ? previousSelectExpression.AddInnerJoin(tableExpression, projection) : previousSelectExpression.AddLeftOuterJoin(tableExpression, projection); joinExpression.Predicate = predicate; - if (outerJoin) + if (groupJoin) { var outerJoinOrderingExtractor = new OuterJoinOrderingExtractor(); @@ -717,6 +716,61 @@ var joinExpression previousSelectExpression .AddToOrderBy(new Ordering(expression, OrderingDirection.Asc)); } + + var additionalFromClause + = queryModel.BodyClauses.ElementAtOrDefault(index + 1) + as AdditionalFromClause; + + var subQueryModel + = (additionalFromClause?.FromExpression as SubQueryExpression)?.QueryModel; + + if (subQueryModel != null + && subQueryModel.ResultOperators.Count == 1 + && subQueryModel.ResultOperators[0] is DefaultIfEmptyResultOperator) + { + var groupJoinClause + = (subQueryModel.MainFromClause.FromExpression as QuerySourceReferenceExpression) + ?.ReferencedQuerySource as GroupJoinClause; + + if (groupJoinClause?.JoinClause == joinClause + && queryModel.CountQuerySourceReferences(groupJoinClause) == 1) + { + queryModel.BodyClauses.RemoveAt(index + 1); + + var querySourceMapping = new QuerySourceMapping(); + + querySourceMapping.AddMapping( + additionalFromClause, + new QuerySourceReferenceExpression(joinClause)); + + queryModel.TransformExpressions(e => + ReferenceReplacingExpressionVisitor + .ReplaceClauseReferences( + e, + querySourceMapping, + throwOnUnmappedReferences: false)); + + Expression = ((MethodCallExpression)Expression).Arguments[0]; + + CurrentParameter = previousParameter; + + foreach (var mapping in previousMapping) + { + QueryCompilationContext.QuerySourceMapping + .ReplaceMapping(mapping.Key, mapping.Value); + } + + var previousProjectionCount = previousSelectExpression.Projection.Count; + + base.VisitJoinClause(joinClause, queryModel, index); + + previousSelectExpression.RemoveRangeFromProjection(previousProjectionCount); + + QueriesBySource.Remove(joinClause); + + operatorToFlatten = LinqOperatorProvider.Join; + } + } } Expression @@ -739,18 +793,40 @@ var joinExpression } } - private bool CanFlattenGroupJoin() + private Dictionary SnapshotQuerySourceMapping(QueryModel queryModel) { - var groupJoinExpression = Expression as MethodCallExpression; - if (groupJoinExpression == null - || !groupJoinExpression.Method.MethodIsClosedFormOf(LinqOperatorProvider.GroupJoin) - || !IsShapedQueryExpression(groupJoinExpression.Arguments[0] as MethodCallExpression, innerShapedQuery: false) - || !IsShapedQueryExpression(groupJoinExpression.Arguments[1] as MethodCallExpression, innerShapedQuery: true)) + var previousMapping + = new Dictionary + { + { + queryModel.MainFromClause, + QueryCompilationContext.QuerySourceMapping + .GetExpression(queryModel.MainFromClause) + } + }; + + foreach (var querySource in queryModel.BodyClauses.OfType()) { - return false; + if (QueryCompilationContext.QuerySourceMapping.ContainsMapping(querySource)) + { + previousMapping.Add( + querySource, + QueryCompilationContext.QuerySourceMapping + .GetExpression(querySource)); + } } - return true; + return previousMapping; + } + + private bool CanFlattenGroupJoin() + { + var groupJoinExpression = Expression as MethodCallExpression; + + return groupJoinExpression != null + && groupJoinExpression.Method.MethodIsClosedFormOf(LinqOperatorProvider.GroupJoin) + && IsShapedQueryExpression(groupJoinExpression.Arguments[0] as MethodCallExpression, innerShapedQuery: false) + && IsShapedQueryExpression(groupJoinExpression.Arguments[1] as MethodCallExpression, innerShapedQuery: true); } private class OuterJoinOrderingExtractor : ExpressionVisitor diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs index 329332b0210..30aed183692 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/Sql/DefaultQuerySqlGenerator.cs @@ -15,6 +15,7 @@ using Microsoft.EntityFrameworkCore.Utilities; using Remotion.Linq.Clauses; using Remotion.Linq.Parsing; +// ReSharper disable SwitchStatementMissingSomeCases namespace Microsoft.EntityFrameworkCore.Query.Sql { @@ -318,12 +319,15 @@ var newExpression newExpression = new PredicateReductionExpressionOptimizer().Visit(newExpression); newExpression = new PredicateNegationExpressionOptimizer().Visit(newExpression); newExpression = new ReducingExpressionVisitor().Visit(newExpression); + var searchConditionTranslatingVisitor = new SearchConditionTranslatingVisitor(searchCondition); + newExpression = searchConditionTranslatingVisitor.Visit(newExpression); if (searchCondition && !SearchConditionTranslatingVisitor.IsSearchCondition(newExpression)) { var constantExpression = newExpression as ConstantExpression; + if ((constantExpression != null) && (bool)constantExpression.Value) { @@ -1144,7 +1148,7 @@ protected override Expression VisitBinary(BinaryExpression expression) ?? parentTypeMapping; } - var needParens = expression.Left is BinaryExpression; + var needParens = expression.Left.RemoveConvert() is BinaryExpression; if (needParens) { @@ -1160,7 +1164,7 @@ protected override Expression VisitBinary(BinaryExpression expression) _relationalCommandBuilder.Append(GenerateOperator(expression)); - needParens = expression.Right is BinaryExpression; + needParens = expression.Right.RemoveConvert() is BinaryExpression; if (needParens) { @@ -1397,6 +1401,7 @@ protected override Expression VisitUnary(UnaryExpression expression) case ExpressionType.Negate: { _relationalCommandBuilder.Append("-"); + Visit(expression.Operand); return expression; @@ -1660,18 +1665,13 @@ public static bool IsSearchCondition(Expression expression) return false; } - if (expression.IsComparisonOperation() - || expression.IsLogicalOperation() - || expression is LikeExpression - || expression is IsNullExpression - || expression is InExpression - || expression is ExistsExpression - || expression is StringCompareExpression) - { - return true; - } - - return false; + return expression.IsComparisonOperation() + || expression.IsLogicalOperation() + || expression is LikeExpression + || expression is IsNullExpression + || expression is InExpression + || expression is ExistsExpression + || expression is StringCompareExpression; } protected override Expression VisitBinary(BinaryExpression expression) @@ -1683,9 +1683,12 @@ protected override Expression VisitBinary(BinaryExpression expression) || expression.NodeType == ExpressionType.And) { var parentIsSearchCondition = _isSearchCondition; + _isSearchCondition = false; + var left = Visit(expression.Left); var right = Visit(expression.Right); + _isSearchCondition = parentIsSearchCondition; return Expression.MakeBinary(expression.NodeType, left, right); @@ -1695,14 +1698,18 @@ protected override Expression VisitBinary(BinaryExpression expression) { Expression newLeft; Expression newRight; + if (expression.IsLogicalOperation() || expression.NodeType == ExpressionType.Or || expression.NodeType == ExpressionType.And) { var parentIsSearchCondition = _isSearchCondition; + _isSearchCondition = expression.IsLogicalOperation(); + newLeft = Visit(expression.Left); newRight = Visit(expression.Right); + _isSearchCondition = parentIsSearchCondition; } else @@ -1711,11 +1718,15 @@ protected override Expression VisitBinary(BinaryExpression expression) newRight = Visit(expression.Right); } - var newExpression = expression.Update(newLeft, expression.Conversion, newRight); + var newExpression + = expression.Update(newLeft, expression.Conversion, newRight); + if (IsSearchCondition(newExpression)) { return Expression.Condition( - newExpression, + newExpression.Type == typeof(bool) + ? (Expression)newExpression + : Expression.Convert(newExpression, typeof(bool)), Expression.Constant(true, typeof(bool)), Expression.Constant(false, typeof(bool))); } @@ -1727,11 +1738,16 @@ protected override Expression VisitBinary(BinaryExpression expression) protected override Expression VisitConditional(ConditionalExpression expression) { var parentIsSearchCondition = _isSearchCondition; + _isSearchCondition = true; + var test = Visit(expression.Test); + _isSearchCondition = false; + var ifTrue = Visit(expression.IfTrue); var ifFalse = Visit(expression.IfFalse); + _isSearchCondition = parentIsSearchCondition; var newExpression = Expression.Condition(test, ifTrue, ifFalse); @@ -1743,12 +1759,14 @@ protected override Expression VisitConditional(ConditionalExpression expression) newExpression, Expression.Constant(true, typeof(bool))); } + return newExpression; } protected override Expression VisitUnary(UnaryExpression expression) { var parentIsSearchCondition = _isSearchCondition; + if (expression.NodeType == ExpressionType.Convert) { _isSearchCondition = false; @@ -1772,33 +1790,40 @@ protected override Expression VisitUnary(UnaryExpression expression) if (expression.NodeType == ExpressionType.Not && expression.Operand.IsSimpleExpression()) { - return Expression.Equal(expression.Operand, Expression.Constant(false, typeof(bool))); + return Expression.Equal( + expression.Operand, + Expression.Constant(false, typeof(bool))); } if (expression.NodeType == ExpressionType.Convert && operand.IsSimpleExpression()) { - return Expression.Equal( - Expression.Convert(operand, expression.Type), - Expression.Constant(true, typeof(bool))); + var equalExpression + = Expression.Equal( + operand.Type != typeof(bool) + ? Expression.Convert(operand, typeof(bool)) + : operand, + Expression.Constant(true, typeof(bool))); + + return equalExpression.Type == expression.Type + ? (Expression)equalExpression + : Expression.Convert(equalExpression, expression.Type); } } else { if (IsSearchCondition(expression)) { - if (expression.NodeType == ExpressionType.Not) - { - return Expression.Condition( - operand, - Expression.Constant(false, typeof(bool)), - Expression.Constant(true, typeof(bool))); - } - - if (expression.NodeType == ExpressionType.Convert - || expression.NodeType == ExpressionType.ConvertChecked) + switch (expression.NodeType) { - return Expression.MakeUnary(expression.NodeType, operand, expression.Type); + case ExpressionType.Not: + return Expression.Condition( + operand, + Expression.Constant(false, typeof(bool)), + Expression.Constant(true, typeof(bool))); + case ExpressionType.Convert: + case ExpressionType.ConvertChecked: + return Expression.MakeUnary(expression.NodeType, operand, expression.Type); } return Expression.Condition( @@ -1816,25 +1841,28 @@ protected override Expression VisitExtension(Expression expression) if (_isSearchCondition) { var parentIsSearchCondition = _isSearchCondition; + _isSearchCondition = false; + var newExpression = base.VisitExtension(expression); + _isSearchCondition = parentIsSearchCondition; - return expression is AliasExpression || expression is ColumnExpression || expression is SelectExpression + return expression is AliasExpression + || expression is ColumnExpression + || expression is SelectExpression ? Expression.Equal(newExpression, Expression.Constant(true, typeof(bool))) : newExpression; } - else + + if (IsSearchCondition(expression)) { - if (IsSearchCondition(expression)) - { - var newExpression = base.VisitExtension(expression); + var newExpression = base.VisitExtension(expression); - return Expression.Condition( - newExpression, - Expression.Constant(true), - Expression.Constant(false)); - } + return Expression.Condition( + newExpression, + Expression.Constant(true), + Expression.Constant(false)); } return base.VisitExtension(expression); @@ -1843,6 +1871,7 @@ protected override Expression VisitExtension(Expression expression) protected override Expression VisitParameter(ParameterExpression expression) { var newExpression = base.VisitParameter(expression); + return _isSearchCondition && (newExpression.Type == typeof(bool)) ? Expression.Equal(newExpression, Expression.Constant(true, typeof(bool))) : newExpression; diff --git a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs index 1e7f3721155..eff90404f36 100644 --- a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs +++ b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs @@ -15,6 +15,8 @@ using Xunit; // ReSharper disable InconsistentNaming // ReSharper disable AccessToDisposedClosure +// ReSharper disable StringCompareIsCultureSpecific.1 +// ReSharper disable StringEndsWithIsCultureSpecific // ReSharper disable ReplaceWithSingleCallToCount // ReSharper disable StringStartsWithIsCultureSpecific @@ -4955,8 +4957,7 @@ join o in os on c.CustomerID equals o.CustomerID into orders from o in orders.DefaultIfEmpty() select o); } - - + [ConditionalFact] public virtual void GroupJoin_Where() { @@ -4991,6 +4992,18 @@ from o in orders.DefaultIfEmpty() select o); } + [ConditionalFact] + public virtual void Join_GroupJoin_DefaultIfEmpty_Where() + { + AssertQuery((cs, os) => + from c in cs + join o in os on c.CustomerID equals o.CustomerID + join o2 in os on c.CustomerID equals o2.CustomerID into orders + from o3 in orders.DefaultIfEmpty() + where o3 != null && o3.CustomerID == "ALFKI" + select o3); + } + [ConditionalFact] public virtual void GroupJoin_DefaultIfEmpty_Project() { @@ -6173,10 +6186,7 @@ public virtual void Subquery_member_pushdown_does_not_change_original_subquery_m .Take(10)); } - protected NorthwindContext CreateContext() - { - return Fixture.CreateContext(); - } + protected NorthwindContext CreateContext() => Fixture.CreateContext(); protected QueryTestBase(TFixture fixture) { diff --git a/src/Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.csproj b/src/Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.csproj index 01c0e1390a8..45840b161b7 100644 --- a/src/Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.csproj +++ b/src/Microsoft.EntityFrameworkCore/Microsoft.EntityFrameworkCore.csproj @@ -268,6 +268,7 @@ + diff --git a/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs b/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs index 518e09635c1..fcbaaf1ed36 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs @@ -806,11 +806,7 @@ var innerItemParameter = Expression.Parameter( innerSequenceExpression.Type.GetSequenceType(), joinClause.ItemName); - if (!_queryCompilationContext.QuerySourceMapping.ContainsMapping(joinClause)) - { - _queryCompilationContext.QuerySourceMapping - .AddMapping(joinClause, innerItemParameter); - } + AddOrUpdateMapping(joinClause, innerItemParameter); var innerKeySelectorExpression = ReplaceClauseReferences(joinClause.InnerKeySelector, joinClause); @@ -880,16 +876,7 @@ var innerItemParameter innerSequenceExpression.Type.GetSequenceType(), groupJoinClause.JoinClause.ItemName); - if (!_queryCompilationContext.QuerySourceMapping.ContainsMapping(groupJoinClause.JoinClause)) - { - _queryCompilationContext.QuerySourceMapping - .AddMapping(groupJoinClause.JoinClause, innerItemParameter); - } - else - { - _queryCompilationContext.QuerySourceMapping - .ReplaceMapping(groupJoinClause.JoinClause, innerItemParameter); - } + AddOrUpdateMapping(groupJoinClause.JoinClause, innerItemParameter); var innerKeySelectorExpression = ReplaceClauseReferences(groupJoinClause.JoinClause.InnerKeySelector, groupJoinClause); @@ -1112,7 +1099,7 @@ var outerAccessExpression = AccessOuterTransparentField(transparentIdentifierType, CurrentParameter); RescopeTransparentAccess(queryModel.MainFromClause, outerAccessExpression); - + for (var i = 0; i < index; i++) { var querySource = queryModel.BodyClauses[i] as IQuerySource; @@ -1120,6 +1107,15 @@ var outerAccessExpression if (querySource != null) { RescopeTransparentAccess(querySource, outerAccessExpression); + + var groupJoinClause = querySource as GroupJoinClause; + + if (groupJoinClause != null + && QueryCompilationContext.QuerySourceMapping + .ContainsMapping(groupJoinClause.JoinClause)) + { + RescopeTransparentAccess(groupJoinClause.JoinClause, outerAccessExpression); + } } } @@ -1145,9 +1141,20 @@ private static Expression ShiftMemberAccess(Expression targetExpression, Express return targetExpression; } - return Expression.MakeMemberAccess( - ShiftMemberAccess(targetExpression, memberExpression.Expression), - memberExpression.Member); + try + { + return Expression.MakeMemberAccess( + ShiftMemberAccess(targetExpression, memberExpression.Expression), + memberExpression.Member); + } + catch (ArgumentException) + { + // Member is not defined on the new target expression. + // This is due to stale QuerySourceMappings, which we can't + // remove due to there not being an API on QuerySourceMapping. + } + + return currentExpression; } #endregion @@ -1210,14 +1217,7 @@ public virtual void AddOrUpdateMapping( Check.NotNull(querySource, nameof(querySource)); Check.NotNull(expression, nameof(expression)); - if (!_queryCompilationContext.QuerySourceMapping.ContainsMapping(querySource)) - { - _queryCompilationContext.QuerySourceMapping.AddMapping(querySource, expression); - } - else - { - _queryCompilationContext.QuerySourceMapping.ReplaceMapping(querySource, expression); - } + QueryCompilationContext.AddOrUpdateMapping(querySource, expression); } #region Binding @@ -1422,9 +1422,12 @@ private IReadOnlyList IterateCompositePropertyExpression( querySourceReferenceExpression = null; while (memberExpression?.Expression != null - || (IsPropertyMethod(methodCallExpression?.Method) && methodCallExpression?.Arguments?[0] != null)) + || (IsPropertyMethod(methodCallExpression?.Method) + && methodCallExpression?.Arguments[0] != null)) { - var propertyName = memberExpression?.Member.Name ?? (string)(methodCallExpression.Arguments[1] as ConstantExpression)?.Value; + var propertyName = memberExpression?.Member.Name + ?? (string)(methodCallExpression.Arguments[1] as ConstantExpression)?.Value; + expression = memberExpression?.Expression ?? methodCallExpression.Arguments[0]; // in case of inheritance there might be convert to derived type here, so we want to check it first diff --git a/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs b/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs index 3467895ed2f..526d5f5c6b6 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/MemberAccessBindingExpressionVisitor.cs @@ -9,6 +9,7 @@ using Microsoft.EntityFrameworkCore.Internal; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Metadata.Internal; +using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; using Microsoft.EntityFrameworkCore.Storage; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; @@ -134,6 +135,33 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) return expression; } + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + protected override Expression VisitExtension(Expression node) + { + var nullConditionalExpression = node as NullConditionalExpression; + + if (nullConditionalExpression != null) + { + var newCaller = Visit(nullConditionalExpression.Caller); + + if (newCaller != nullConditionalExpression.Caller + && newCaller.Type == typeof(ValueBuffer)) + { + var newAccessOperation = Visit(nullConditionalExpression.AccessOperation); + + if (newAccessOperation != nullConditionalExpression.AccessOperation) + { + return newAccessOperation; + } + } + } + + return base.VisitExtension(node); + } + /// /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. diff --git a/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/RequiresMaterializationExpressionVisitor.cs b/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/RequiresMaterializationExpressionVisitor.cs index e99ce785733..3bb91ccc01a 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/RequiresMaterializationExpressionVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/RequiresMaterializationExpressionVisitor.cs @@ -6,7 +6,6 @@ using System.Linq.Expressions; using JetBrains.Annotations; using Microsoft.EntityFrameworkCore.Metadata; -using Microsoft.EntityFrameworkCore.Query.Expressions.Internal; using Remotion.Linq; using Remotion.Linq.Clauses; using Remotion.Linq.Clauses.Expressions; @@ -219,7 +218,8 @@ var resultQuerySource _querySources[querySourceReferenceExpression.ReferencedQuerySource]--; } - foreach (var sourceExpression in _queryModel.ResultOperators.Select(SetResultOperationSourceExpression).Where(e => e != null)) + foreach (var sourceExpression + in _queryModel.ResultOperators.Select(SetResultOperationSourceExpression).Where(e => e != null)) { if (sourceExpression.Equals(expression)) { @@ -258,13 +258,8 @@ private static Expression SetResultOperationSourceExpression(ResultOperatorBase } var unionOperator = resultOperator as UnionResultOperator; - // ReSharper disable once UseNullPropagation - if (unionOperator != null) - { - return unionOperator.Source2; - } - - return null; + + return unionOperator?.Source2; } } } diff --git a/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryModelExtensions.cs b/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryModelExtensions.cs new file mode 100644 index 00000000000..9f56b5c0de0 --- /dev/null +++ b/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryModelExtensions.cs @@ -0,0 +1,62 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Linq.Expressions; +using JetBrains.Annotations; +using Microsoft.EntityFrameworkCore.Query.ExpressionVisitors; +using Remotion.Linq; +using Remotion.Linq.Clauses; +using Remotion.Linq.Clauses.Expressions; + +namespace Microsoft.EntityFrameworkCore.Query.Internal +{ + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public static class QueryModelExtensions + { + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public static int CountQuerySourceReferences( + [NotNull] this QueryModel queryModel, [NotNull] IQuerySource querySource) + { + var visitor = new ReferenceFindingExpressionVisitor(querySource); + + queryModel.TransformExpressions(visitor.Visit); + + return visitor.Count; + } + + private class ReferenceFindingExpressionVisitor : ExpressionVisitorBase + { + private readonly IQuerySource _querySource; + + public ReferenceFindingExpressionVisitor(IQuerySource querySource) + { + _querySource = querySource; + } + + public int Count { get; private set; } + + protected override Expression VisitQuerySourceReference(QuerySourceReferenceExpression expression) + { + if (expression.ReferencedQuerySource == _querySource) + { + Count++; + } + + return expression; + } + + protected override Expression VisitSubQuery(SubQueryExpression expression) + { + expression.QueryModel.TransformExpressions(Visit); + + return expression; + } + } + } +} diff --git a/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryOptimizer.cs b/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryOptimizer.cs index 5aa1765afa7..a6b48a4e49e 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryOptimizer.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/Internal/QueryOptimizer.cs @@ -68,6 +68,24 @@ public virtual void Optimize( /// directly from your code. This API may change or be removed in future releases. /// public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, int index) + { + TryFlattenJoin(joinClause, queryModel); + + base.VisitJoinClause(joinClause, queryModel, index); + } + + /// + /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// directly from your code. This API may change or be removed in future releases. + /// + public override void VisitJoinClause(JoinClause joinClause, QueryModel queryModel, GroupJoinClause groupJoinClause) + { + TryFlattenJoin(joinClause, queryModel); + + base.VisitJoinClause(joinClause, queryModel, groupJoinClause); + } + + private void TryFlattenJoin(JoinClause joinClause, QueryModel queryModel) { var subQueryExpression = joinClause.InnerSequence as SubQueryExpression; @@ -90,8 +108,6 @@ in _queryAnnotations } } } - - base.VisitJoinClause(joinClause, queryModel, index); } /// @@ -114,30 +130,7 @@ var querySourceReferenceExpression if (querySourceReferenceExpression != null && querySourceReferenceExpression.ReferencedQuerySource == groupJoinClause) { - var referenceCount = 0; - - Func groupReferenceFinder = null; - - groupReferenceFinder - = e => - { - var qsre = e as QuerySourceReferenceExpression; - - if (qsre?.ReferencedQuerySource == groupJoinClause) - { - referenceCount++; - } - - var sq = e as SubQueryExpression; - - sq?.QueryModel.TransformExpressions(groupReferenceFinder); - - return e; - }; - - queryModel.TransformExpressions(groupReferenceFinder); - - if (referenceCount == 1) + if (queryModel.CountQuerySourceReferences(groupJoinClause) == 1) { // GroupJoin/SelectMany can be rewritten to regular Join. diff --git a/src/Microsoft.EntityFrameworkCore/Query/QueryCompilationContext.cs b/src/Microsoft.EntityFrameworkCore/Query/QueryCompilationContext.cs index 42f9d7bf6fe..8643c584205 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/QueryCompilationContext.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/QueryCompilationContext.cs @@ -36,7 +36,7 @@ public class QueryCompilationContext private ISet _querySourcesRequiringMaterialization; /// - /// This API supports the Entity Framework Core infrastructure and is not intended to be used + /// This API supports the Entity Framework Core infrastructure and is not intended to be used /// directly from your code. This API may change or be removed in future releases. /// public QueryCompilationContext( @@ -113,6 +113,27 @@ public QueryCompilationContext( /// public virtual QuerySourceMapping QuerySourceMapping { get; } = new QuerySourceMapping(); + /// + /// Adds or updates the expression mapped to a query source. + /// + /// The query source. + /// The expression mapped to the query source. + public virtual void AddOrUpdateMapping( + [NotNull] IQuerySource querySource, [NotNull] Expression expression) + { + Check.NotNull(querySource, nameof(querySource)); + Check.NotNull(expression, nameof(expression)); + + if (!QuerySourceMapping.ContainsMapping(querySource)) + { + QuerySourceMapping.AddMapping(querySource, expression); + } + else + { + QuerySourceMapping.ReplaceMapping(querySource, expression); + } + } + /// /// Gets the query annotations./ /// @@ -259,7 +280,7 @@ public virtual EntityQueryModelVisitor CreateQueryModelVisitor() /// The new query model visitor. /// public virtual EntityQueryModelVisitor CreateQueryModelVisitor( - [CanBeNull] EntityQueryModelVisitor parentEntityQueryModelVisitor) + [CanBeNull] EntityQueryModelVisitor parentEntityQueryModelVisitor) => _entityQueryModelVisitorFactory.Create(this, parentEntityQueryModelVisitor); /// diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs index d9041b4a219..c65362b4eae 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs @@ -32,6 +32,7 @@ public override void Entity_equality_empty() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Optional_FK].[Id], [l.OneToOne_Optional_FK].[Date], [l.OneToOne_Optional_FK].[Level1_Optional_Id], [l.OneToOne_Optional_FK].[Level1_Required_Id], [l.OneToOne_Optional_FK].[Name], [l.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Optional_FK] ON [l].[Id] = [l.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE [l.OneToOne_Optional_FK].[Id] = 0 ORDER BY [l].[Id]", Sql); } @@ -44,6 +45,7 @@ public override void Key_equality_when_sentinel_ef_property() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Optional_FK].[Id], [l.OneToOne_Optional_FK].[Date], [l.OneToOne_Optional_FK].[Level1_Optional_Id], [l.OneToOne_Optional_FK].[Level1_Required_Id], [l.OneToOne_Optional_FK].[Name], [l.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Optional_FK] ON [l].[Id] = [l.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE [l.OneToOne_Optional_FK].[Id] = 0 ORDER BY [l].[Id]", Sql); } @@ -56,6 +58,7 @@ public override void Key_equality_using_property_method_required() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Required_FK].[Id], [l.OneToOne_Required_FK].[Date], [l.OneToOne_Required_FK].[Level1_Optional_Id], [l.OneToOne_Required_FK].[Level1_Required_Id], [l.OneToOne_Required_FK].[Name], [l.OneToOne_Required_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Required_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Required_FK] ON [l].[Id] = [l.OneToOne_Required_FK].[Level1_Required_Id] +WHERE [l.OneToOne_Required_FK].[Id] > 7 ORDER BY [l].[Id]", Sql); } @@ -79,6 +82,7 @@ public override void Key_equality_using_property_method_nested() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Required_FK].[Id], [l.OneToOne_Required_FK].[Date], [l.OneToOne_Required_FK].[Level1_Optional_Id], [l.OneToOne_Required_FK].[Level1_Required_Id], [l.OneToOne_Required_FK].[Name], [l.OneToOne_Required_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Required_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Required_FK] ON [l].[Id] = [l.OneToOne_Required_FK].[Level1_Required_Id] +WHERE [l.OneToOne_Required_FK].[Id] = 7 ORDER BY [l].[Id]", Sql); } @@ -102,8 +106,9 @@ public override void Key_equality_using_property_method_and_member_expression1() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Required_FK].[Id], [l.OneToOne_Required_FK].[Date], [l.OneToOne_Required_FK].[Level1_Optional_Id], [l.OneToOne_Required_FK].[Level1_Required_Id], [l.OneToOne_Required_FK].[Name], [l.OneToOne_Required_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Required_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Required_FK] ON [l].[Id] = [l.OneToOne_Required_FK].[Level1_Required_Id] +WHERE [l.OneToOne_Required_FK].[Id] = 7 ORDER BY [l].[Id]", - Sql); + Sql); } public override void Key_equality_using_property_method_and_member_expression2() @@ -114,6 +119,7 @@ public override void Key_equality_using_property_method_and_member_expression2() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Required_FK].[Id], [l.OneToOne_Required_FK].[Date], [l.OneToOne_Required_FK].[Level1_Optional_Id], [l.OneToOne_Required_FK].[Level1_Required_Id], [l.OneToOne_Required_FK].[Name], [l.OneToOne_Required_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Required_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Required_FK] ON [l].[Id] = [l.OneToOne_Required_FK].[Level1_Required_Id] +WHERE [l.OneToOne_Required_FK].[Id] = 7 ORDER BY [l].[Id]", Sql); } @@ -149,6 +155,7 @@ public override void Key_equality_two_conditions_on_same_navigation() @"SELECT [l].[Id], [l].[Date], [l].[Name], [l].[OneToMany_Optional_Self_InverseId], [l].[OneToMany_Required_Self_InverseId], [l].[OneToOne_Optional_SelfId], [l.OneToOne_Required_FK].[Id], [l.OneToOne_Required_FK].[Date], [l.OneToOne_Required_FK].[Level1_Optional_Id], [l.OneToOne_Required_FK].[Level1_Required_Id], [l.OneToOne_Required_FK].[Name], [l.OneToOne_Required_FK].[OneToMany_Optional_InverseId], [l.OneToOne_Required_FK].[OneToMany_Optional_Self_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_InverseId], [l.OneToOne_Required_FK].[OneToMany_Required_Self_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_PK_InverseId], [l.OneToOne_Required_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l] LEFT JOIN [Level2] AS [l.OneToOne_Required_FK] ON [l].[Id] = [l.OneToOne_Required_FK].[Level1_Required_Id] +WHERE ([l.OneToOne_Required_FK].[Id] = 1) OR ([l.OneToOne_Required_FK].[Id] = 2) ORDER BY [l].[Id]", Sql); } @@ -313,6 +320,7 @@ public override void Navigation_key_access_optional_comparison() @"SELECT [e2].[Id], [e2].[Date], [e2].[Level1_Optional_Id], [e2].[Level1_Required_Id], [e2].[Name], [e2].[OneToMany_Optional_InverseId], [e2].[OneToMany_Optional_Self_InverseId], [e2].[OneToMany_Required_InverseId], [e2].[OneToMany_Required_Self_InverseId], [e2].[OneToOne_Optional_PK_InverseId], [e2].[OneToOne_Optional_SelfId], [e2.OneToOne_Optional_PK_Inverse].[Id], [e2.OneToOne_Optional_PK_Inverse].[Date], [e2.OneToOne_Optional_PK_Inverse].[Name], [e2.OneToOne_Optional_PK_Inverse].[OneToMany_Optional_Self_InverseId], [e2.OneToOne_Optional_PK_Inverse].[OneToMany_Required_Self_InverseId], [e2.OneToOne_Optional_PK_Inverse].[OneToOne_Optional_SelfId] FROM [Level2] AS [e2] LEFT JOIN [Level1] AS [e2.OneToOne_Optional_PK_Inverse] ON [e2].[OneToOne_Optional_PK_InverseId] = [e2.OneToOne_Optional_PK_Inverse].[Id] +WHERE [e2.OneToOne_Optional_PK_Inverse].[Id] > 5 ORDER BY [e2].[OneToOne_Optional_PK_InverseId]", Sql); } @@ -371,6 +379,7 @@ public override void Optional_navigation_inside_property_method_translated_to_jo @"SELECT [e1].[Id], [e1].[Date], [e1].[Name], [e1].[OneToMany_Optional_Self_InverseId], [e1].[OneToMany_Required_Self_InverseId], [e1].[OneToOne_Optional_SelfId], [e1.OneToOne_Optional_FK].[Id], [e1.OneToOne_Optional_FK].[Date], [e1.OneToOne_Optional_FK].[Level1_Optional_Id], [e1.OneToOne_Optional_FK].[Level1_Required_Id], [e1.OneToOne_Optional_FK].[Name], [e1.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [e1.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [e1.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [e1.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [e1.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [e1.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [e1] LEFT JOIN [Level2] AS [e1.OneToOne_Optional_FK] ON [e1].[Id] = [e1.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE [e1.OneToOne_Optional_FK].[Name] = N'L2 01' ORDER BY [e1].[Id]", Sql); } @@ -561,6 +570,8 @@ public override void Join_navigation_translated_to_subquery_nested() { base.Join_navigation_translated_to_subquery_nested(); + // Separate asserts to account for ordering differences on .NETCore + Assert.Contains( @"SELECT [e1].[Id] FROM [Level1] AS [e1]", @@ -569,17 +580,12 @@ public override void Join_navigation_translated_to_subquery_nested() Assert.Contains( @"@_outer_Id: ? -SELECT [subQuery.OneToOne_Optional_FK].[Id], [subQuery.OneToOne_Optional_FK].[Level2_Optional_Id], [subQuery.OneToOne_Optional_FK].[Level2_Required_Id], [subQuery.OneToOne_Optional_FK].[Name], [subQuery.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [subQuery.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [subQuery.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [subQuery.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [subQuery.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [subQuery.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] +SELECT TOP(1) [subQuery.OneToOne_Optional_FK].[Id], [subQuery.OneToOne_Optional_FK].[Level2_Optional_Id], [subQuery.OneToOne_Optional_FK].[Level2_Required_Id], [subQuery.OneToOne_Optional_FK].[Name], [subQuery.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [subQuery.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [subQuery.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [subQuery.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [subQuery.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [subQuery.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level2] AS [subQuery] LEFT JOIN [Level3] AS [subQuery.OneToOne_Optional_FK] ON [subQuery].[Id] = [subQuery.OneToOne_Optional_FK].[Level2_Optional_Id] WHERE [subQuery].[Level1_Required_Id] = @_outer_Id ORDER BY [subQuery].[Id]", Sql); - - Assert.Contains( - @"SELECT [e3].[Id] -FROM [Level3] AS [e3]", - Sql); } public override void Join_navigation_translated_to_subquery_deeply_nested_non_key_join() @@ -770,6 +776,7 @@ public override void Where_nav_prop_reference_optional1() @"SELECT [e].[Id], [e].[Date], [e].[Name], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_SelfId], [e.OneToOne_Optional_FK].[Id], [e.OneToOne_Optional_FK].[Date], [e.OneToOne_Optional_FK].[Level1_Optional_Id], [e.OneToOne_Optional_FK].[Level1_Required_Id], [e.OneToOne_Optional_FK].[Name], [e.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [e.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [e.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [e] LEFT JOIN [Level2] AS [e.OneToOne_Optional_FK] ON [e].[Id] = [e.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE [e.OneToOne_Optional_FK].[Name] IN (N'L2 05', N'L2 07') ORDER BY [e].[Id]", Sql); } @@ -799,6 +806,7 @@ public override void Where_nav_prop_reference_optional2() @"SELECT [e].[Id], [e].[Date], [e].[Name], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_SelfId], [e.OneToOne_Optional_FK].[Id], [e.OneToOne_Optional_FK].[Date], [e.OneToOne_Optional_FK].[Level1_Optional_Id], [e.OneToOne_Optional_FK].[Level1_Required_Id], [e.OneToOne_Optional_FK].[Name], [e.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [e.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [e.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [e] LEFT JOIN [Level2] AS [e.OneToOne_Optional_FK] ON [e].[Id] = [e.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE (([e.OneToOne_Optional_FK].[Name] = N'L2 05') AND [e.OneToOne_Optional_FK].[Name] IS NOT NULL) OR (([e.OneToOne_Optional_FK].[Name] <> N'L2 42') OR [e.OneToOne_Optional_FK].[Name] IS NULL) ORDER BY [e].[Id]", Sql); } @@ -896,6 +904,7 @@ public override void Where_multiple_nav_prop_reference_optional_compared_to_null @"SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name], [l3].[OneToMany_Optional_InverseId], [l3].[OneToMany_Optional_Self_InverseId], [l3].[OneToMany_Required_InverseId], [l3].[OneToMany_Required_Self_InverseId], [l3].[OneToOne_Optional_PK_InverseId], [l3].[OneToOne_Optional_SelfId], [l3.OneToOne_Optional_FK_Inverse].[Id], [l3.OneToOne_Optional_FK_Inverse].[Date], [l3.OneToOne_Optional_FK_Inverse].[Level1_Optional_Id], [l3.OneToOne_Optional_FK_Inverse].[Level1_Required_Id], [l3.OneToOne_Optional_FK_Inverse].[Name], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Optional_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Optional_Self_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Required_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Required_Self_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToOne_Optional_PK_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToOne_Optional_SelfId] FROM [Level3] AS [l3] LEFT JOIN [Level2] AS [l3.OneToOne_Optional_FK_Inverse] ON [l3].[Level2_Optional_Id] = [l3.OneToOne_Optional_FK_Inverse].[Id] +WHERE [l3.OneToOne_Optional_FK_Inverse].[Level1_Optional_Id] IS NULL ORDER BY [l3].[Level2_Optional_Id]", Sql); } @@ -925,6 +934,7 @@ public override void Where_multiple_nav_prop_reference_optional_compared_to_null @"SELECT [l3].[Id], [l3].[Level2_Optional_Id], [l3].[Level2_Required_Id], [l3].[Name], [l3].[OneToMany_Optional_InverseId], [l3].[OneToMany_Optional_Self_InverseId], [l3].[OneToMany_Required_InverseId], [l3].[OneToMany_Required_Self_InverseId], [l3].[OneToOne_Optional_PK_InverseId], [l3].[OneToOne_Optional_SelfId], [l3.OneToOne_Optional_FK_Inverse].[Id], [l3.OneToOne_Optional_FK_Inverse].[Date], [l3.OneToOne_Optional_FK_Inverse].[Level1_Optional_Id], [l3.OneToOne_Optional_FK_Inverse].[Level1_Required_Id], [l3.OneToOne_Optional_FK_Inverse].[Name], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Optional_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Optional_Self_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Required_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToMany_Required_Self_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToOne_Optional_PK_InverseId], [l3.OneToOne_Optional_FK_Inverse].[OneToOne_Optional_SelfId] FROM [Level3] AS [l3] LEFT JOIN [Level2] AS [l3.OneToOne_Optional_FK_Inverse] ON [l3].[Level2_Optional_Id] = [l3.OneToOne_Optional_FK_Inverse].[Id] +WHERE [l3.OneToOne_Optional_FK_Inverse].[Level1_Optional_Id] IS NOT NULL ORDER BY [l3].[Level2_Optional_Id]", Sql); } @@ -1137,11 +1147,12 @@ public override void Complex_navigations_with_predicate_projected_into_anonymous base.Complex_navigations_with_predicate_projected_into_anonymous_type2(); Assert.Equal( - @"SELECT [e].[Id], [e].[Level2_Optional_Id], [e].[Level2_Required_Id], [e].[Name], [e].[OneToMany_Optional_InverseId], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_PK_InverseId], [e].[OneToOne_Optional_SelfId], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Id], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Date], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Name], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[OneToMany_Optional_Self_InverseId], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[OneToMany_Required_Self_InverseId], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[OneToOne_Optional_SelfId], [e.OneToOne_Required_FK_Inverse.OneToOne_Required_FK_Inverse].[Id] + @"SELECT [e].[Id], [e].[Level2_Optional_Id], [e].[Level2_Required_Id], [e].[Name], [e].[OneToMany_Optional_InverseId], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_PK_InverseId], [e].[OneToOne_Optional_SelfId], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Id], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Date], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Name], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[OneToMany_Optional_Self_InverseId], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[OneToMany_Required_Self_InverseId], [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[OneToOne_Optional_SelfId] FROM [Level3] AS [e] INNER JOIN [Level2] AS [e.OneToOne_Required_FK_Inverse] ON [e].[Level2_Required_Id] = [e.OneToOne_Required_FK_Inverse].[Id] INNER JOIN [Level1] AS [e.OneToOne_Required_FK_Inverse.OneToOne_Required_FK_Inverse] ON [e.OneToOne_Required_FK_Inverse].[Level1_Required_Id] = [e.OneToOne_Required_FK_Inverse.OneToOne_Required_FK_Inverse].[Id] LEFT JOIN [Level1] AS [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse] ON [e.OneToOne_Required_FK_Inverse].[Level1_Optional_Id] = [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Id] +WHERE ([e.OneToOne_Required_FK_Inverse.OneToOne_Required_FK_Inverse].[Id] = [e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Id]) AND ([e.OneToOne_Required_FK_Inverse.OneToOne_Optional_FK_Inverse].[Id] <> 7) ORDER BY [e.OneToOne_Required_FK_Inverse].[Level1_Optional_Id]", Sql); } @@ -1166,7 +1177,7 @@ public override void OrderBy_nav_prop_reference_optional() @"SELECT [e].[Id], [e].[Date], [e].[Name], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_SelfId], [e.OneToOne_Optional_FK].[Id], [e.OneToOne_Optional_FK].[Date], [e.OneToOne_Optional_FK].[Level1_Optional_Id], [e.OneToOne_Optional_FK].[Level1_Required_Id], [e.OneToOne_Optional_FK].[Name], [e.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [e.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [e.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [e.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [e] LEFT JOIN [Level2] AS [e.OneToOne_Optional_FK] ON [e].[Id] = [e.OneToOne_Optional_FK].[Level1_Optional_Id] -ORDER BY [e].[Id]", +ORDER BY [e.OneToOne_Optional_FK].[Name], [e].[Id]", Sql); } @@ -1178,7 +1189,7 @@ public override void OrderBy_nav_prop_reference_optional_via_DefaultIfEmpty() @"SELECT [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_InverseId], [l2].[OneToMany_Optional_Self_InverseId], [l2].[OneToMany_Required_InverseId], [l2].[OneToMany_Required_Self_InverseId], [l2].[OneToOne_Optional_PK_InverseId], [l2].[OneToOne_Optional_SelfId] FROM [Level1] AS [l1] LEFT JOIN [Level2] AS [l2] ON [l1].[Id] = [l2].[Level1_Optional_Id] -ORDER BY [l1].[Id]", +ORDER BY [l2].[Name], [l1].[Id]", Sql); } @@ -1215,6 +1226,7 @@ public override void Include_with_optional_navigation() FROM [Level1] AS [e] LEFT JOIN [Level2] AS [e.OneToOne_Optional_FK] ON [e].[Id] = [e.OneToOne_Optional_FK].[Level1_Optional_Id] LEFT JOIN [Level2] AS [l] ON [l].[Level1_Optional_Id] = [e].[Id] +WHERE ([e.OneToOne_Optional_FK].[Name] <> N'L2 05') OR [e.OneToOne_Optional_FK].[Name] IS NULL ORDER BY [e].[Id]", Sql); } @@ -1228,6 +1240,7 @@ public override void Include_nested_with_optional_navigation() FROM [Level1] AS [e] LEFT JOIN [Level2] AS [e.OneToOne_Optional_FK] ON [e].[Id] = [e.OneToOne_Optional_FK].[Level1_Optional_Id] LEFT JOIN [Level2] AS [l] ON [l].[Level1_Optional_Id] = [e].[Id] +WHERE ([e.OneToOne_Optional_FK].[Name] <> N'L2 09') OR [e.OneToOne_Optional_FK].[Name] IS NULL ORDER BY [e].[Id], [l].[Id] SELECT [l0].[Id], [l0].[Level2_Optional_Id], [l0].[Level2_Required_Id], [l0].[Name], [l0].[OneToMany_Optional_InverseId], [l0].[OneToMany_Optional_Self_InverseId], [l0].[OneToMany_Required_InverseId], [l0].[OneToMany_Required_Self_InverseId], [l0].[OneToOne_Optional_PK_InverseId], [l0].[OneToOne_Optional_SelfId], [l2].[Id], [l2].[Level3_Optional_Id], [l2].[Level3_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_InverseId], [l2].[OneToMany_Optional_Self_InverseId], [l2].[OneToMany_Required_InverseId], [l2].[OneToMany_Required_Self_InverseId], [l2].[OneToOne_Optional_PK_InverseId], [l2].[OneToOne_Optional_SelfId] @@ -1237,6 +1250,7 @@ INNER JOIN ( FROM [Level1] AS [e] LEFT JOIN [Level2] AS [e.OneToOne_Optional_FK] ON [e].[Id] = [e.OneToOne_Optional_FK].[Level1_Optional_Id] LEFT JOIN [Level2] AS [l] ON [l].[Level1_Optional_Id] = [e].[Id] + WHERE ([e.OneToOne_Optional_FK].[Name] <> N'L2 09') OR [e.OneToOne_Optional_FK].[Name] IS NULL ) AS [l1] ON [l0].[OneToMany_Required_InverseId] = [l1].[Id0] LEFT JOIN [Level4] AS [l2] ON [l2].[Level3_Required_Id] = [l0].[Id] ORDER BY [l1].[Id], [l1].[Id0]", @@ -1253,13 +1267,10 @@ public override void Include_with_groupjoin_skip_and_take() @"@__p_0: ? @__p_1: ? -SELECT [e].[Id], [e].[Date], [e].[Name], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_SelfId], [t].[Id], [t].[Date], [t].[Level1_Optional_Id], [t].[Level1_Required_Id], [t].[Name], [t].[OneToMany_Optional_InverseId], [t].[OneToMany_Optional_Self_InverseId], [t].[OneToMany_Required_InverseId], [t].[OneToMany_Required_Self_InverseId], [t].[OneToOne_Optional_PK_InverseId], [t].[OneToOne_Optional_SelfId], [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_InverseId], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_PK_InverseId], [l1].[OneToOne_Optional_SelfId] +SELECT [e].[Id], [e].[Date], [e].[Name], [e].[OneToMany_Optional_Self_InverseId], [e].[OneToMany_Required_Self_InverseId], [e].[OneToOne_Optional_SelfId], [l2].[Id], [l2].[Date], [l2].[Level1_Optional_Id], [l2].[Level1_Required_Id], [l2].[Name], [l2].[OneToMany_Optional_InverseId], [l2].[OneToMany_Optional_Self_InverseId], [l2].[OneToMany_Required_InverseId], [l2].[OneToMany_Required_Self_InverseId], [l2].[OneToOne_Optional_PK_InverseId], [l2].[OneToOne_Optional_SelfId], [l1].[Id], [l1].[Level2_Optional_Id], [l1].[Level2_Required_Id], [l1].[Name], [l1].[OneToMany_Optional_InverseId], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_PK_InverseId], [l1].[OneToOne_Optional_SelfId] FROM [Level1] AS [e] -LEFT JOIN ( - SELECT [e1].[Id], [e1].[Date], [e1].[Level1_Optional_Id], [e1].[Level1_Required_Id], [e1].[Name], [e1].[OneToMany_Optional_InverseId], [e1].[OneToMany_Optional_Self_InverseId], [e1].[OneToMany_Required_InverseId], [e1].[OneToMany_Required_Self_InverseId], [e1].[OneToOne_Optional_PK_InverseId], [e1].[OneToOne_Optional_SelfId] - FROM [Level2] AS [e1] -) AS [t] ON [e].[Id] = [t].[Level1_Optional_Id] -LEFT JOIN [Level3] AS [l1] ON [l1].[Id] = [t].[Id] +LEFT JOIN [Level2] AS [l2] ON [e].[Id] = [l2].[Level1_Optional_Id] +LEFT JOIN [Level3] AS [l1] ON [l1].[Id] = [l2].[Id] WHERE ([e].[Name] <> N'L1 03') OR [e].[Name] IS NULL ORDER BY [e].[Id] OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY @@ -1273,10 +1284,7 @@ FROM [Level2] AS [l] WHERE EXISTS ( SELECT 1 FROM [Level1] AS [e] - LEFT JOIN ( - SELECT [e1].[Id], [e1].[Date], [e1].[Level1_Optional_Id], [e1].[Level1_Required_Id], [e1].[Name], [e1].[OneToMany_Optional_InverseId], [e1].[OneToMany_Optional_Self_InverseId], [e1].[OneToMany_Required_InverseId], [e1].[OneToMany_Required_Self_InverseId], [e1].[OneToOne_Optional_PK_InverseId], [e1].[OneToOne_Optional_SelfId] - FROM [Level2] AS [e1] - ) AS [t] ON [e].[Id] = [t].[Level1_Optional_Id] + LEFT JOIN [Level2] AS [l2] ON [e].[Id] = [l2].[Level1_Optional_Id] WHERE (([e].[Name] <> N'L1 03') OR [e].[Name] IS NULL) AND (([l].[OneToMany_Optional_InverseId] = [e].[Id]) AND [l].[OneToMany_Optional_InverseId] IS NOT NULL) ORDER BY @@ROWCOUNT OFFSET @__p_0 ROWS FETCH NEXT @__p_1 ROWS ONLY) @@ -1310,14 +1318,16 @@ FROM [Level1] AS [l1] public override void Query_source_materialization_bug_4547() { base.Query_source_materialization_bug_4547(); - + + // Separate asserts to account for ordering differences on .NETCore + Assert.Contains( @"SELECT [e1].[Id] -FROM [Level1] AS [e1]", +FROM [Level1] AS [e1]", Sql); Assert.Contains( - @"SELECT [subQuery2].[Id], [subQuery2].[Date], [subQuery2].[Level1_Optional_Id], [subQuery2].[Level1_Required_Id], [subQuery2].[Name], [subQuery2].[OneToMany_Optional_InverseId], [subQuery2].[OneToMany_Optional_Self_InverseId], [subQuery2].[OneToMany_Required_InverseId], [subQuery2].[OneToMany_Required_Self_InverseId], [subQuery2].[OneToOne_Optional_PK_InverseId], [subQuery2].[OneToOne_Optional_SelfId], [subQuery3].[Id], [subQuery3].[Level2_Optional_Id], [subQuery3].[Level2_Required_Id], [subQuery3].[Name], [subQuery3].[OneToMany_Optional_InverseId], [subQuery3].[OneToMany_Optional_Self_InverseId], [subQuery3].[OneToMany_Required_InverseId], [subQuery3].[OneToMany_Required_Self_InverseId], [subQuery3].[OneToOne_Optional_PK_InverseId], [subQuery3].[OneToOne_Optional_SelfId] + @"SELECT TOP(1) [subQuery2].[Id], [subQuery2].[Date], [subQuery2].[Level1_Optional_Id], [subQuery2].[Level1_Required_Id], [subQuery2].[Name], [subQuery2].[OneToMany_Optional_InverseId], [subQuery2].[OneToMany_Optional_Self_InverseId], [subQuery2].[OneToMany_Required_InverseId], [subQuery2].[OneToMany_Required_Self_InverseId], [subQuery2].[OneToOne_Optional_PK_InverseId], [subQuery2].[OneToOne_Optional_SelfId], [subQuery3].[Id], [subQuery3].[Level2_Optional_Id], [subQuery3].[Level2_Required_Id], [subQuery3].[Name], [subQuery3].[OneToMany_Optional_InverseId], [subQuery3].[OneToMany_Optional_Self_InverseId], [subQuery3].[OneToMany_Required_InverseId], [subQuery3].[OneToMany_Required_Self_InverseId], [subQuery3].[OneToOne_Optional_PK_InverseId], [subQuery3].[OneToOne_Optional_SelfId] FROM [Level2] AS [subQuery2] LEFT JOIN [Level3] AS [subQuery3] ON [subQuery2].[Id] = [subQuery3].[Level2_Optional_Id] ORDER BY [subQuery2].[Id]", @@ -1475,14 +1485,16 @@ public override void Where_navigation_property_to_collection() { base.Where_navigation_property_to_collection(); - Assert.StartsWith( + Assert.Equal( @"SELECT [l1].[Id], [l1].[Date], [l1].[Name], [l1].[OneToMany_Optional_Self_InverseId], [l1].[OneToMany_Required_Self_InverseId], [l1].[OneToOne_Optional_SelfId], [l1.OneToOne_Required_FK].[Id], [l1.OneToOne_Required_FK].[Date], [l1.OneToOne_Required_FK].[Level1_Optional_Id], [l1.OneToOne_Required_FK].[Level1_Required_Id], [l1.OneToOne_Required_FK].[Name], [l1.OneToOne_Required_FK].[OneToMany_Optional_InverseId], [l1.OneToOne_Required_FK].[OneToMany_Optional_Self_InverseId], [l1.OneToOne_Required_FK].[OneToMany_Required_InverseId], [l1.OneToOne_Required_FK].[OneToMany_Required_Self_InverseId], [l1.OneToOne_Required_FK].[OneToOne_Optional_PK_InverseId], [l1.OneToOne_Required_FK].[OneToOne_Optional_SelfId] FROM [Level1] AS [l1] LEFT JOIN [Level2] AS [l1.OneToOne_Required_FK] ON [l1].[Id] = [l1.OneToOne_Required_FK].[Level1_Required_Id] -ORDER BY [l1].[Id] - -SELECT [l0].[OneToMany_Optional_InverseId] -FROM [Level3] AS [l0]", +WHERE ( + SELECT COUNT(*) + FROM [Level3] AS [l] + WHERE [l1.OneToOne_Required_FK].[Id] = [l].[OneToMany_Optional_InverseId] +) > 0 +ORDER BY [l1].[Id]", Sql); } diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs index c56dc656552..945250a5128 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs @@ -12,6 +12,12 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests { public class GearsOfWarQuerySqlServerTest : GearsOfWarQueryTestBase { + public GearsOfWarQuerySqlServerTest(GearsOfWarQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + //TestSqlLoggerFactory.CaptureOutput(testOutputHelper); + } + public override void Entity_equality_empty() { base.Entity_equality_empty(); @@ -295,6 +301,7 @@ SELECT [g].* FROM [Gear] AS [g] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') ) AS [g] ON ([o].[GearNickName] = [g].[Nickname]) AND ([o].[GearSquadId] = [g].[SquadId]) +WHERE [o.Gear].[Nickname] = N'Marcus' ORDER BY [o].[GearNickName], [o].[GearSquadId]", Sql); } @@ -371,21 +378,24 @@ public override void Include_where_list_contains_navigation() { base.Include_where_list_contains_navigation(); + // Separate asserts to account for ordering differences on .NETCore + Assert.Contains( @"SELECT [t].[Id] FROM [CogTag] AS [t]", Sql); Assert.Contains( - @"SELECT [g.Tag0].[Id], [g.Tag0].[GearNickName], [g.Tag0].[GearSquadId], [g.Tag0].[Note] -FROM [CogTag] AS [g.Tag0]", Sql); + @"SELECT [g.Tag1].[Id], [g.Tag1].[GearNickName], [g.Tag1].[GearSquadId], [g.Tag1].[Note] +FROM [CogTag] AS [g.Tag1]", + Sql); Assert.Contains( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [g.Tag].[Id], [g.Tag].[GearNickName], [g.Tag].[GearSquadId], [g.Tag].[Note], [c].[Id], [c].[GearNickName], [c].[GearSquadId], [c].[Note] FROM [Gear] AS [g] LEFT JOIN [CogTag] AS [g.Tag] ON ([g].[Nickname] = [g.Tag].[GearNickName]) AND ([g].[SquadId] = [g.Tag].[GearSquadId]) LEFT JOIN [CogTag] AS [c] ON ([c].[GearNickName] = [g].[Nickname]) AND ([c].[GearSquadId] = [g].[SquadId]) -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND [g.Tag].[Id] IS NOT NULL ORDER BY [g].[Nickname], [g].[SquadId]", Sql); } @@ -581,11 +591,11 @@ public override void Where_bitwise_and_enum() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g].[Rank] & 1 > 0) +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (([g].[Rank] & 1) > 0) SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g].[Rank] & 1 = 1)", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (([g].[Rank] & 1) = 1)", Sql); } @@ -621,7 +631,7 @@ public override void Where_bitwise_and_nullable_enum_with_constant() Assert.Equal( @"SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] -WHERE [w].[AmmunitionType] & 1 > 0", +WHERE ([w].[AmmunitionType] & 1) > 0", Sql); } @@ -633,7 +643,7 @@ public override void Where_bitwise_and_nullable_enum_with_null_constant() Assert.Equal( @"SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] -WHERE [w].[AmmunitionType] & NULL > 0", +WHERE ([w].[AmmunitionType] & NULL) > 0", Sql); } @@ -647,7 +657,7 @@ public override void Where_bitwise_and_nullable_enum_with_non_nullable_parameter SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] -WHERE [w].[AmmunitionType] & @__ammunitionType_0 > 0", +WHERE ([w].[AmmunitionType] & @__ammunitionType_0) > 0", Sql); } @@ -661,13 +671,13 @@ public override void Where_bitwise_and_nullable_enum_with_nullable_parameter() SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] -WHERE [w].[AmmunitionType] & @__ammunitionType_0 > 0 +WHERE ([w].[AmmunitionType] & @__ammunitionType_0) > 0 @__ammunitionType_0: (DbType = Int32) SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] -WHERE [w].[AmmunitionType] & @__ammunitionType_0 > 0", +WHERE ([w].[AmmunitionType] & @__ammunitionType_0) > 0", Sql); } @@ -679,7 +689,7 @@ public override void Where_bitwise_or_enum() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g].[Rank] | 1 > 0)", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (([g].[Rank] | 1) > 0)", Sql); } @@ -690,14 +700,14 @@ public override void Bitwise_projects_values_in_select() Assert.Equal( @"SELECT TOP(1) CASE - WHEN [g].[Rank] & 1 = 1 + WHEN ([g].[Rank] & 1) = 1 THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END, CASE - WHEN [g].[Rank] & 1 = 2 + WHEN ([g].[Rank] & 1) = 2 THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) END, [g].[Rank] & 1 FROM [Gear] AS [g] -WHERE (([g].[Discriminator] = N'Officer') OR ([g].[Discriminator] = N'Gear')) AND ([g].[Rank] & 1 = 1)", +WHERE (([g].[Discriminator] = N'Officer') OR ([g].[Discriminator] = N'Gear')) AND (([g].[Rank] & 1) = 1)", Sql); } @@ -976,7 +986,7 @@ public override void Null_propagation_optimization1() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND [g].[LeaderNickname] = N'Marcus'", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g].[LeaderNickname] = N'Marcus')", Sql); } @@ -987,7 +997,7 @@ public override void Null_propagation_optimization2() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND RIGHT([g].[LeaderNickname], LEN(N'us')) = N'us'", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (RIGHT([g].[LeaderNickname], LEN(N'us')) = N'us')", Sql); } @@ -999,7 +1009,7 @@ public override void Null_propagation_optimization3() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND RIGHT([g].[LeaderNickname], LEN(N'us')) = N'us'", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (RIGHT([g].[LeaderNickname], LEN(N'us')) = N'us')", Sql); } @@ -1010,7 +1020,7 @@ public override void Null_propagation_optimization4() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND LEN([g].[LeaderNickname]) = 5", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (LEN([g].[LeaderNickname]) = 5)", Sql); } @@ -1021,7 +1031,7 @@ public override void Null_propagation_optimization5() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND LEN([g].[LeaderNickname]) = 5", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (LEN([g].[LeaderNickname]) = 5)", Sql); } @@ -1032,7 +1042,7 @@ public override void Null_propagation_optimization6() Assert.Equal( @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank] FROM [Gear] AS [g] -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND LEN([g].[LeaderNickname]) = 5", +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND (LEN([g].[LeaderNickname]) = 5)", Sql); } @@ -1097,6 +1107,7 @@ public override void Select_Singleton_Navigation_With_Member_Access() @"SELECT [ct].[Id], [ct].[GearNickName], [ct].[GearSquadId], [ct].[Note], [ct.Gear].[Nickname], [ct.Gear].[SquadId], [ct.Gear].[AssignedCityName], [ct.Gear].[CityOrBirthName], [ct.Gear].[Discriminator], [ct.Gear].[FullName], [ct.Gear].[HasSoulPatch], [ct.Gear].[LeaderNickname], [ct.Gear].[LeaderSquadId], [ct.Gear].[Rank] FROM [CogTag] AS [ct] LEFT JOIN [Gear] AS [ct.Gear] ON ([ct].[GearNickName] = [ct.Gear].[Nickname]) AND ([ct].[GearSquadId] = [ct.Gear].[SquadId]) +WHERE ([ct.Gear].[Nickname] = N'Marcus') AND ([ct.Gear].[CityOrBirthName] <> N'Ephyra') ORDER BY [ct].[GearNickName], [ct].[GearSquadId]", Sql); } @@ -1109,6 +1120,7 @@ public override void Select_Where_Navigation() @"SELECT [ct].[Id], [ct].[GearNickName], [ct].[GearSquadId], [ct].[Note], [ct.Gear].[Nickname], [ct.Gear].[SquadId], [ct.Gear].[AssignedCityName], [ct.Gear].[CityOrBirthName], [ct.Gear].[Discriminator], [ct.Gear].[FullName], [ct.Gear].[HasSoulPatch], [ct.Gear].[LeaderNickname], [ct.Gear].[LeaderSquadId], [ct.Gear].[Rank] FROM [CogTag] AS [ct] LEFT JOIN [Gear] AS [ct.Gear] ON ([ct].[GearNickName] = [ct.Gear].[Nickname]) AND ([ct].[GearSquadId] = [ct.Gear].[SquadId]) +WHERE [ct.Gear].[Nickname] = N'Marcus' ORDER BY [ct].[GearNickName], [ct].[GearSquadId]", Sql); } @@ -1195,10 +1207,11 @@ public override void Optional_Navigation_Null_Coalesce_To_Clr_Type() { base.Optional_Navigation_Null_Coalesce_To_Clr_Type(); - Assert.Equal(@"SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId], [w.SynergyWith].[Id], [w.SynergyWith].[AmmunitionType], [w.SynergyWith].[IsAutomatic], [w.SynergyWith].[Name], [w.SynergyWith].[OwnerFullName], [w.SynergyWith].[SynergyWithId] + Assert.Equal(@"SELECT TOP(1) [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId], [w.SynergyWith].[Id], [w.SynergyWith].[AmmunitionType], [w.SynergyWith].[IsAutomatic], [w.SynergyWith].[Name], [w.SynergyWith].[OwnerFullName], [w.SynergyWith].[SynergyWithId] FROM [Weapon] AS [w] LEFT JOIN [Weapon] AS [w.SynergyWith] ON [w].[SynergyWithId] = [w.SynergyWith].[Id] -ORDER BY [w].[SynergyWithId]", Sql); +ORDER BY [w].[SynergyWithId]", + Sql); } public override void Where_subquery_boolean() @@ -1224,6 +1237,7 @@ public override void Singleton_Navigation_With_Member_Access() @"SELECT [ct].[Id], [ct].[GearNickName], [ct].[GearSquadId], [ct].[Note], [ct.Gear].[Nickname], [ct.Gear].[SquadId], [ct.Gear].[AssignedCityName], [ct.Gear].[CityOrBirthName], [ct.Gear].[Discriminator], [ct.Gear].[FullName], [ct.Gear].[HasSoulPatch], [ct.Gear].[LeaderNickname], [ct.Gear].[LeaderSquadId], [ct.Gear].[Rank] FROM [CogTag] AS [ct] LEFT JOIN [Gear] AS [ct.Gear] ON ([ct].[GearNickName] = [ct.Gear].[Nickname]) AND ([ct].[GearSquadId] = [ct.Gear].[SquadId]) +WHERE ([ct.Gear].[Nickname] = N'Marcus') AND ([ct.Gear].[CityOrBirthName] <> N'Ephyra') ORDER BY [ct].[GearNickName], [ct].[GearSquadId]", Sql); } @@ -1409,29 +1423,21 @@ public override void Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesc base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result2(); Assert.Equal( - @"SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOrBirthName], [t].[Discriminator], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank] + @"SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank], [g2].[Nickname], [g2].[SquadId], [g2].[AssignedCityName], [g2].[CityOrBirthName], [g2].[Discriminator], [g2].[FullName], [g2].[HasSoulPatch], [g2].[LeaderNickname], [g2].[LeaderSquadId], [g2].[Rank] FROM [Gear] AS [g1] -LEFT JOIN ( - SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOrBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank] - FROM [Gear] AS [g0] - WHERE [g0].[Discriminator] IN (N'Officer', N'Gear') -) AS [t] ON [g1].[LeaderNickname] = [t].[Nickname] +LEFT JOIN [Gear] AS [g2] ON [g1].[LeaderNickname] = [g2].[Nickname] WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') -ORDER BY [g1].[LeaderNickname], [t].[FullName] +ORDER BY [g1].[LeaderNickname], [g2].[FullName] SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] INNER JOIN ( - SELECT DISTINCT [g1].[LeaderNickname], [t].[FullName] + SELECT DISTINCT [g1].[LeaderNickname], [g2].[FullName] FROM [Gear] AS [g1] - LEFT JOIN ( - SELECT [g0].[Nickname], [g0].[SquadId], [g0].[AssignedCityName], [g0].[CityOrBirthName], [g0].[Discriminator], [g0].[FullName], [g0].[HasSoulPatch], [g0].[LeaderNickname], [g0].[LeaderSquadId], [g0].[Rank] - FROM [Gear] AS [g0] - WHERE [g0].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g1].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g1].[LeaderNickname] = [g2].[Nickname] WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [w].[OwnerFullName] = [t0].[FullName] -ORDER BY [t0].[LeaderNickname], [t0].[FullName]", +) AS [g21] ON [w].[OwnerFullName] = [g21].[FullName] +ORDER BY [g21].[LeaderNickname], [g21].[FullName]", Sql); } @@ -1440,43 +1446,31 @@ public override void Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesc base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_coalesce_result3(); Assert.Equal( - @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOrBirthName], [t].[Discriminator], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank] + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [g2].[Nickname], [g2].[SquadId], [g2].[AssignedCityName], [g2].[CityOrBirthName], [g2].[Discriminator], [g2].[FullName], [g2].[HasSoulPatch], [g2].[LeaderNickname], [g2].[LeaderSquadId], [g2].[Rank] FROM [Gear] AS [g] -LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') -) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] +LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -ORDER BY [g].[LeaderNickname], [g].[FullName], [t].[FullName] +ORDER BY [g].[LeaderNickname], [g].[FullName], [g2].[FullName] SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] INNER JOIN ( SELECT DISTINCT [g].[LeaderNickname], [g].[FullName] FROM [Gear] AS [g] - LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [g2] ON [w].[OwnerFullName] = [g2].[FullName] -ORDER BY [g2].[LeaderNickname], [g2].[FullName] +) AS [g0] ON [w].[OwnerFullName] = [g0].[FullName] +ORDER BY [g0].[LeaderNickname], [g0].[FullName] SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId] FROM [Weapon] AS [w0] INNER JOIN ( - SELECT DISTINCT [g].[LeaderNickname], [g].[FullName], [t].[FullName] AS [FullName0] + SELECT DISTINCT [g].[LeaderNickname], [g].[FullName], [g2].[FullName] AS [FullName0] FROM [Gear] AS [g] - LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [w0].[OwnerFullName] = [t0].[FullName0] -ORDER BY [t0].[LeaderNickname], [t0].[FullName], [t0].[FullName0]", +) AS [g21] ON [w0].[OwnerFullName] = [g21].[FullName0] +ORDER BY [g21].[LeaderNickname], [g21].[FullName], [g21].[FullName0]", Sql); } @@ -1506,8 +1500,8 @@ FROM [Gear] AS [g1] WHERE [g1].[Discriminator] = N'Officer' ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [g2] ON [w].[OwnerFullName] = [g2].[FullName] -ORDER BY [g2].[LeaderNickname], [g2].[FullName] +) AS [g4] ON [w].[OwnerFullName] = [g4].[FullName] +ORDER BY [g4].[LeaderNickname], [g4].[FullName] SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId] FROM [Weapon] AS [w0] @@ -1520,8 +1514,8 @@ FROM [Gear] AS [g1] WHERE [g1].[Discriminator] = N'Officer' ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [w0].[OwnerFullName] = [t0].[FullName0] -ORDER BY [t0].[LeaderNickname], [t0].[FullName], [t0].[FullName0]", +) AS [t1] ON [w0].[OwnerFullName] = [t1].[FullName0] +ORDER BY [t1].[LeaderNickname], [t1].[FullName], [t1].[FullName0]", Sql); } @@ -1530,43 +1524,31 @@ public override void Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_conditi base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_conditional_result(); Assert.Equal( - @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOrBirthName], [t].[Discriminator], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank] + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [g2].[Nickname], [g2].[SquadId], [g2].[AssignedCityName], [g2].[CityOrBirthName], [g2].[Discriminator], [g2].[FullName], [g2].[HasSoulPatch], [g2].[LeaderNickname], [g2].[LeaderSquadId], [g2].[Rank] FROM [Gear] AS [g] -LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') -) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] +LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -ORDER BY [g].[LeaderNickname], [g].[FullName], [t].[FullName] +ORDER BY [g].[LeaderNickname], [g].[FullName], [g2].[FullName] SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] INNER JOIN ( SELECT DISTINCT [g].[LeaderNickname], [g].[FullName] FROM [Gear] AS [g] - LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [g2] ON [w].[OwnerFullName] = [g2].[FullName] -ORDER BY [g2].[LeaderNickname], [g2].[FullName] +) AS [g0] ON [w].[OwnerFullName] = [g0].[FullName] +ORDER BY [g0].[LeaderNickname], [g0].[FullName] SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId] FROM [Weapon] AS [w0] INNER JOIN ( - SELECT DISTINCT [g].[LeaderNickname], [g].[FullName], [t].[FullName] AS [FullName0] + SELECT DISTINCT [g].[LeaderNickname], [g].[FullName], [g2].[FullName] AS [FullName0] FROM [Gear] AS [g] - LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [w0].[OwnerFullName] = [t0].[FullName0] -ORDER BY [t0].[LeaderNickname], [t0].[FullName], [t0].[FullName0]", +) AS [g21] ON [w0].[OwnerFullName] = [g21].[FullName0] +ORDER BY [g21].[LeaderNickname], [g21].[FullName], [g21].[FullName0]", Sql); } @@ -1575,43 +1557,31 @@ public override void Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_complex base.Include_on_GroupJoin_SelectMany_DefaultIfEmpty_with_complex_projection_result(); Assert.Equal( - @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [t].[Nickname], [t].[SquadId], [t].[AssignedCityName], [t].[CityOrBirthName], [t].[Discriminator], [t].[FullName], [t].[HasSoulPatch], [t].[LeaderNickname], [t].[LeaderSquadId], [t].[Rank] + @"SELECT [g].[Nickname], [g].[SquadId], [g].[AssignedCityName], [g].[CityOrBirthName], [g].[Discriminator], [g].[FullName], [g].[HasSoulPatch], [g].[LeaderNickname], [g].[LeaderSquadId], [g].[Rank], [g2].[Nickname], [g2].[SquadId], [g2].[AssignedCityName], [g2].[CityOrBirthName], [g2].[Discriminator], [g2].[FullName], [g2].[HasSoulPatch], [g2].[LeaderNickname], [g2].[LeaderSquadId], [g2].[Rank] FROM [Gear] AS [g] -LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') -) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] +LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -ORDER BY [g].[LeaderNickname], [g].[FullName], [t].[FullName] +ORDER BY [g].[LeaderNickname], [g].[FullName], [g2].[FullName] SELECT [w].[Id], [w].[AmmunitionType], [w].[IsAutomatic], [w].[Name], [w].[OwnerFullName], [w].[SynergyWithId] FROM [Weapon] AS [w] INNER JOIN ( SELECT DISTINCT [g].[LeaderNickname], [g].[FullName] FROM [Gear] AS [g] - LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [g2] ON [w].[OwnerFullName] = [g2].[FullName] -ORDER BY [g2].[LeaderNickname], [g2].[FullName] +) AS [g0] ON [w].[OwnerFullName] = [g0].[FullName] +ORDER BY [g0].[LeaderNickname], [g0].[FullName] SELECT [w0].[Id], [w0].[AmmunitionType], [w0].[IsAutomatic], [w0].[Name], [w0].[OwnerFullName], [w0].[SynergyWithId] FROM [Weapon] AS [w0] INNER JOIN ( - SELECT DISTINCT [g].[LeaderNickname], [g].[FullName], [t].[FullName] AS [FullName0] + SELECT DISTINCT [g].[LeaderNickname], [g].[FullName], [g2].[FullName] AS [FullName0] FROM [Gear] AS [g] - LEFT JOIN ( - SELECT [g1].[Nickname], [g1].[SquadId], [g1].[AssignedCityName], [g1].[CityOrBirthName], [g1].[Discriminator], [g1].[FullName], [g1].[HasSoulPatch], [g1].[LeaderNickname], [g1].[LeaderSquadId], [g1].[Rank] - FROM [Gear] AS [g1] - WHERE [g1].[Discriminator] IN (N'Officer', N'Gear') - ) AS [t] ON [g].[LeaderNickname] = [t].[Nickname] + LEFT JOIN [Gear] AS [g2] ON [g].[LeaderNickname] = [g2].[Nickname] WHERE [g].[Discriminator] IN (N'Officer', N'Gear') -) AS [t0] ON [w0].[OwnerFullName] = [t0].[FullName0] -ORDER BY [t0].[LeaderNickname], [t0].[FullName], [t0].[FullName0]", +) AS [g21] ON [w0].[OwnerFullName] = [g21].[FullName0] +ORDER BY [g21].[LeaderNickname], [g21].[FullName], [g21].[FullName0]", Sql); } @@ -1658,6 +1628,7 @@ public override void Optional_navigation_type_compensation_works_with_predicate( @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) +WHERE (([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL) AND ([t.Gear].[HasSoulPatch] = 1) ORDER BY [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -1670,6 +1641,7 @@ public override void Optional_navigation_type_compensation_works_with_predicate2 @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) +WHERE [t.Gear].[HasSoulPatch] = 1 ORDER BY [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -1682,6 +1654,7 @@ public override void Optional_navigation_type_compensation_works_with_predicate_ @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) +WHERE [t.Gear].[HasSoulPatch] <> 1 ORDER BY [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -1694,6 +1667,10 @@ public override void Optional_navigation_type_compensation_works_with_conditiona @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) +WHERE CASE + WHEN [t.Gear].[HasSoulPatch] = 1 + THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) +END = 1 ORDER BY [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -1706,6 +1683,7 @@ public override void Optional_navigation_type_compensation_works_with_binary_exp @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) +WHERE ([t.Gear].[HasSoulPatch] = 1) OR (CHARINDEX(N'Cole', [t].[Note]) > 0) ORDER BY [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -1784,7 +1762,7 @@ public override void Optional_navigation_type_compensation_works_with_orderby() FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) WHERE ([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL -ORDER BY [t].[GearNickName], [t].[GearSquadId]", +ORDER BY [t.Gear].[SquadId], [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -1797,7 +1775,7 @@ public override void Optional_navigation_type_compensation_works_with_groupby() FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) WHERE ([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL -ORDER BY [t].[GearNickName], [t].[GearSquadId]", +ORDER BY [t.Gear].[SquadId]", Sql); } @@ -1806,11 +1784,14 @@ public override void Optional_navigation_type_compensation_works_with_all() base.Optional_navigation_type_compensation_works_with_all(); Assert.Equal( - @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] -FROM [CogTag] AS [t] -LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) -WHERE ([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL -ORDER BY [t].[GearNickName], [t].[GearSquadId]", + @"SELECT CASE + WHEN NOT EXISTS ( + SELECT 1 + FROM [CogTag] AS [t] + LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) + WHERE (([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL) AND ([t.Gear].[HasSoulPatch] = 0)) + THEN CAST(1 AS BIT) ELSE CAST(0 AS BIT) +END", Sql); } @@ -1822,16 +1803,12 @@ public override void Optional_navigation_type_compensation_works_with_contains() @"SELECT [t].[Id], [t].[GearNickName], [t].[GearSquadId], [t].[Note], [t.Gear].[Nickname], [t.Gear].[SquadId], [t.Gear].[AssignedCityName], [t.Gear].[CityOrBirthName], [t.Gear].[Discriminator], [t.Gear].[FullName], [t.Gear].[HasSoulPatch], [t.Gear].[LeaderNickname], [t.Gear].[LeaderSquadId], [t.Gear].[Rank] FROM [CogTag] AS [t] LEFT JOIN [Gear] AS [t.Gear] ON ([t].[GearNickName] = [t.Gear].[Nickname]) AND ([t].[GearSquadId] = [t.Gear].[SquadId]) -WHERE ([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL -ORDER BY [t].[GearNickName], [t].[GearSquadId] - -SELECT [g0].[SquadId] -FROM [Gear] AS [g0] -WHERE ([g0].[Discriminator] = N'Officer') OR ([g0].[Discriminator] = N'Gear') - -SELECT [g0].[SquadId] -FROM [Gear] AS [g0] -WHERE ([g0].[Discriminator] = N'Officer') OR ([g0].[Discriminator] = N'Gear')", +WHERE (([t].[Note] <> N'K.I.A.') OR [t].[Note] IS NULL) AND [t.Gear].[SquadId] IN ( + SELECT [g].[SquadId] + FROM [Gear] AS [g] + WHERE ([g].[Discriminator] = N'Officer') OR ([g].[Discriminator] = N'Gear') +) +ORDER BY [t].[GearNickName], [t].[GearSquadId]", Sql); } @@ -2028,12 +2005,6 @@ THEN CAST(0 AS BIT) ELSE CAST(1 AS BIT) Sql); } - public GearsOfWarQuerySqlServerTest(GearsOfWarQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - //TestSqlLoggerFactory.CaptureOutput(testOutputHelper); - } - protected override void ClearLog() => TestSqlLoggerFactory.Reset(); private const string FileLineEnding = @" diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs index 78742bcfef6..a3f0107dcee 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs @@ -11,6 +11,12 @@ namespace Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests { public class QueryNavigationsSqlServerTest : QueryNavigationsTestBase { + public QueryNavigationsSqlServerTest(NorthwindQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) + : base(fixture) + { + //TestSqlLoggerFactory.CaptureOutput(testOutputHelper); + } + public override void Select_Where_Navigation() { base.Select_Where_Navigation(); @@ -19,6 +25,7 @@ public override void Select_Where_Navigation() @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +WHERE [o.Customer].[City] = N'Seattle' ORDER BY [o].[CustomerID]", Sql); } @@ -27,11 +34,14 @@ public override void Select_Where_Navigation_Deep() { base.Select_Where_Navigation_Deep(); - Assert.StartsWith( - @"SELECT [od].[OrderID], [od].[ProductID], [od].[Discount], [od].[Quantity], [od].[UnitPrice], [od.Order.Customer].[CustomerID], [od.Order.Customer].[Address], [od.Order.Customer].[City], [od.Order.Customer].[CompanyName], [od.Order.Customer].[ContactName], [od.Order.Customer].[ContactTitle], [od.Order.Customer].[Country], [od.Order.Customer].[Fax], [od.Order.Customer].[Phone], [od.Order.Customer].[PostalCode], [od.Order.Customer].[Region] + Assert.Equal( + @"@__p_0: 1 + +SELECT TOP(@__p_0) [od].[OrderID], [od].[ProductID], [od].[Discount], [od].[Quantity], [od].[UnitPrice], [od.Order.Customer].[CustomerID], [od.Order.Customer].[Address], [od.Order.Customer].[City], [od.Order.Customer].[CompanyName], [od.Order.Customer].[ContactName], [od.Order.Customer].[ContactTitle], [od.Order.Customer].[Country], [od.Order.Customer].[Fax], [od.Order.Customer].[Phone], [od.Order.Customer].[PostalCode], [od.Order.Customer].[Region] FROM [Order Details] AS [od] INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] LEFT JOIN [Customers] AS [od.Order.Customer] ON [od.Order].[CustomerID] = [od.Order.Customer].[CustomerID] +WHERE [od.Order.Customer].[City] = N'Seattle' ORDER BY [od].[OrderID], [od].[ProductID], [od.Order].[CustomerID]", Sql); } @@ -191,6 +201,7 @@ public override void Select_Where_Navigation_Included() FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [o.Customer].[City] = N'Seattle' ORDER BY [o].[CustomerID]", Sql); } @@ -206,6 +217,7 @@ FROM [Order Details] AS [od] LEFT JOIN [Customers] AS [od.Order.Customer] ON [od.Order].[CustomerID] = [od.Order.Customer].[CustomerID] INNER JOIN [Orders] AS [o] ON [od].[OrderID] = [o].[OrderID] LEFT JOIN [Customers] AS [c] ON [o].[CustomerID] = [c].[CustomerID] +WHERE [od.Order.Customer].[City] = N'London' ORDER BY [od.Order].[CustomerID]", Sql); } @@ -242,6 +254,7 @@ public override void Select_Where_Navigation_Multiple_Access() @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +WHERE (([o.Customer].[City] = N'Seattle') AND [o.Customer].[City] IS NOT NULL) AND (([o.Customer].[Phone] <> N'555 555 5555') OR [o.Customer].[Phone] IS NULL) ORDER BY [o].[CustomerID]", Sql); } @@ -254,6 +267,7 @@ public override void Select_Navigations_Where_Navigations() @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +WHERE (([o.Customer].[City] = N'Seattle') AND [o.Customer].[City] IS NOT NULL) AND (([o.Customer].[Phone] <> N'555 555 5555') OR [o.Customer].[Phone] IS NULL) ORDER BY [o].[CustomerID]", Sql); } @@ -278,6 +292,7 @@ public override void Select_Singleton_Navigation_With_Member_Access() @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +WHERE (([o.Customer].[City] = N'Seattle') AND [o.Customer].[City] IS NOT NULL) AND (([o.Customer].[Phone] <> N'555 555 5555') OR [o.Customer].[Phone] IS NULL) ORDER BY [o].[CustomerID]", Sql); } @@ -286,7 +301,7 @@ public override void Select_count_plus_sum() { base.Select_count_plus_sum(); - Assert.StartsWith( + Assert.Equal( @"SELECT ( SELECT SUM([od0].[Quantity]) FROM [Order Details] AS [od0] @@ -308,6 +323,7 @@ public override void Singleton_Navigation_With_Member_Access() @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +WHERE (([o.Customer].[City] = N'Seattle') AND [o.Customer].[City] IS NOT NULL) AND (([o.Customer].[Phone] <> N'555 555 5555') OR [o.Customer].[Phone] IS NULL) ORDER BY [o].[CustomerID]", Sql); } @@ -317,8 +333,8 @@ public override void Select_Where_Navigation_Scalar_Equals_Navigation_Scalar() base.Select_Where_Navigation_Scalar_Equals_Navigation_Scalar(); Assert.Contains( - @"SELECT [o.Customer0].[CustomerID], [o.Customer0].[Address], [o.Customer0].[City], [o.Customer0].[CompanyName], [o.Customer0].[ContactName], [o.Customer0].[ContactTitle], [o.Customer0].[Country], [o.Customer0].[Fax], [o.Customer0].[Phone], [o.Customer0].[PostalCode], [o.Customer0].[Region] -FROM [Customers] AS [o.Customer0]", + @"SELECT [o.Customer1].[CustomerID], [o.Customer1].[Address], [o.Customer1].[City], [o.Customer1].[CompanyName], [o.Customer1].[ContactName], [o.Customer1].[ContactTitle], [o.Customer1].[Country], [o.Customer1].[Fax], [o.Customer1].[Phone], [o.Customer1].[PostalCode], [o.Customer1].[Region] +FROM [Customers] AS [o.Customer1]", Sql); @@ -341,8 +357,8 @@ public override void Select_Where_Navigation_Scalar_Equals_Navigation_Scalar_Pro base.Select_Where_Navigation_Scalar_Equals_Navigation_Scalar_Projected(); Assert.Contains( - @"SELECT [o.Customer0].[CustomerID], [o.Customer0].[Address], [o.Customer0].[City], [o.Customer0].[CompanyName], [o.Customer0].[ContactName], [o.Customer0].[ContactTitle], [o.Customer0].[Country], [o.Customer0].[Fax], [o.Customer0].[Phone], [o.Customer0].[PostalCode], [o.Customer0].[Region] -FROM [Customers] AS [o.Customer0]", + @"SELECT [o.Customer1].[CustomerID], [o.Customer1].[Address], [o.Customer1].[City], [o.Customer1].[CompanyName], [o.Customer1].[ContactName], [o.Customer1].[ContactTitle], [o.Customer1].[Country], [o.Customer1].[Fax], [o.Customer1].[Phone], [o.Customer1].[PostalCode], [o.Customer1].[Region] +FROM [Customers] AS [o.Customer1]", Sql); Assert.Contains( @@ -390,6 +406,7 @@ public override void Select_Where_Navigation_Null_Deep() @"SELECT [e].[EmployeeID], [e].[City], [e].[Country], [e].[FirstName], [e].[ReportsTo], [e].[Title], [e.Manager].[EmployeeID], [e.Manager].[City], [e.Manager].[Country], [e.Manager].[FirstName], [e.Manager].[ReportsTo], [e.Manager].[Title] FROM [Employees] AS [e] LEFT JOIN [Employees] AS [e.Manager] ON [e].[ReportsTo] = [e.Manager].[EmployeeID] +WHERE [e.Manager].[ReportsTo] IS NULL ORDER BY [e].[ReportsTo]", Sql); } @@ -788,7 +805,7 @@ ORDER BY [e].[CustomerID] @_outer_CustomerID: ALFKI (Size = 450) -SELECT [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] +SELECT TOP(1) [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] FROM [Orders] AS [e0] LEFT JOIN [Customers] AS [e.Customer] ON [e0].[CustomerID] = [e.Customer].[CustomerID] WHERE [e0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) AND (@_outer_CustomerID = [e0].[CustomerID]) @@ -796,7 +813,7 @@ ORDER BY [e0].[CustomerID] @_outer_CustomerID: ANATR (Size = 450) -SELECT [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] +SELECT TOP(1) [e.Customer].[CustomerID], [e.Customer].[Address], [e.Customer].[City], [e.Customer].[CompanyName], [e.Customer].[ContactName], [e.Customer].[ContactTitle], [e.Customer].[Country], [e.Customer].[Fax], [e.Customer].[Phone], [e.Customer].[PostalCode], [e.Customer].[Region] FROM [Orders] AS [e0] LEFT JOIN [Customers] AS [e.Customer] ON [e0].[CustomerID] = [e.Customer].[CustomerID] WHERE [e0].[OrderID] IN (10643, 10692, 10702, 10835, 10952, 11011) AND (@_outer_CustomerID = [e0].[CustomerID]) @@ -813,13 +830,7 @@ public override void Collection_select_nav_prop_first_or_default_then_nav_prop_n FROM [Customers] AS [e] WHERE [e].[CustomerID] LIKE N'A' + N'%' AND (CHARINDEX(N'A', [e].[CustomerID]) = 1) -SELECT [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] -FROM [Orders] AS [o] -LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -WHERE [o].[CustomerID] = N'ALFKI' -ORDER BY [o].[CustomerID] - -SELECT [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] +SELECT TOP(1) [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] WHERE [o].[CustomerID] = N'ALFKI' @@ -836,13 +847,7 @@ public override void Collection_select_nav_prop_single_or_default_then_nav_prop_ FROM [Customers] AS [e] WHERE [e].[CustomerID] LIKE N'A' + N'%' AND (CHARINDEX(N'A', [e].[CustomerID]) = 1) -SELECT [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] -FROM [Orders] AS [o] -LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -WHERE [o].[OrderID] = 10643 -ORDER BY [o].[CustomerID] - -SELECT [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] +SELECT TOP(2) [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] WHERE [o].[OrderID] = 10643 @@ -859,13 +864,7 @@ public override void Collection_select_nav_prop_first_or_default_then_nav_prop_n FROM [Customers] AS [e] WHERE [e].[CustomerID] LIKE N'A' + N'%' AND (CHARINDEX(N'A', [e].[CustomerID]) = 1) -SELECT [oo.Customer].[CustomerID], [oo.Customer].[Address], [oo.Customer].[City], [oo.Customer].[CompanyName], [oo.Customer].[ContactName], [oo.Customer].[ContactTitle], [oo.Customer].[Country], [oo.Customer].[Fax], [oo.Customer].[Phone], [oo.Customer].[PostalCode], [oo.Customer].[Region] -FROM [Orders] AS [oo] -LEFT JOIN [Customers] AS [oo.Customer] ON [oo].[CustomerID] = [oo.Customer].[CustomerID] -WHERE [oo].[CustomerID] = N'ALFKI' -ORDER BY [oo].[CustomerID] - -SELECT [oo.Customer].[CustomerID], [oo.Customer].[Address], [oo.Customer].[City], [oo.Customer].[CompanyName], [oo.Customer].[ContactName], [oo.Customer].[ContactTitle], [oo.Customer].[Country], [oo.Customer].[Fax], [oo.Customer].[Phone], [oo.Customer].[PostalCode], [oo.Customer].[Region] +SELECT TOP(1) [oo.Customer].[CustomerID], [oo.Customer].[Address], [oo.Customer].[City], [oo.Customer].[CompanyName], [oo.Customer].[ContactName], [oo.Customer].[ContactTitle], [oo.Customer].[Country], [oo.Customer].[Fax], [oo.Customer].[Phone], [oo.Customer].[PostalCode], [oo.Customer].[Region] FROM [Orders] AS [oo] LEFT JOIN [Customers] AS [oo.Customer] ON [oo].[CustomerID] = [oo.Customer].[CustomerID] WHERE [oo].[CustomerID] = N'ALFKI' @@ -882,7 +881,7 @@ public override void Collection_select_nav_prop_first_or_default_then_nav_prop_n FROM [Customers] AS [e] WHERE [e].[CustomerID] LIKE N'A' + N'%' AND (CHARINDEX(N'A', [e].[CustomerID]) = 1) -SELECT [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] +SELECT TOP(1) [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] WHERE [o].[CustomerID] = N'ALFKI' @@ -1019,17 +1018,18 @@ public override void Navigation_in_subquery_referencing_outer_query() { base.Navigation_in_subquery_referencing_outer_query(); - Assert.StartsWith( + Assert.Equal( @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -ORDER BY [o].[CustomerID] - -SELECT [od.Order.Customer0].[CustomerID], [od.Order.Customer0].[Address], [od.Order.Customer0].[City], [od.Order.Customer0].[CompanyName], [od.Order.Customer0].[ContactName], [od.Order.Customer0].[ContactTitle], [od.Order.Customer0].[Country], [od.Order.Customer0].[Fax], [od.Order.Customer0].[Phone], [od.Order.Customer0].[PostalCode], [od.Order.Customer0].[Region] -FROM [Order Details] AS [od0] -INNER JOIN [Orders] AS [od.Order0] ON [od0].[OrderID] = [od.Order0].[OrderID] -LEFT JOIN [Customers] AS [od.Order.Customer0] ON [od.Order0].[CustomerID] = [od.Order.Customer0].[CustomerID] -ORDER BY [od.Order0].[CustomerID]", +WHERE ( + SELECT COUNT(*) + FROM [Order Details] AS [od] + INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] + LEFT JOIN [Customers] AS [od.Order.Customer] ON [od.Order].[CustomerID] = [od.Order.Customer].[CustomerID] + WHERE ([o.Customer].[Country] = [od.Order.Customer].[Country]) OR ([o.Customer].[Country] IS NULL AND [od.Order.Customer].[Country] IS NULL) +) > 0 +ORDER BY [o].[CustomerID]", Sql); } @@ -1041,7 +1041,7 @@ public override void GroupBy_on_nav_prop() @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -ORDER BY [o].[CustomerID]", +ORDER BY [o.Customer].[City]", Sql); } @@ -1139,7 +1139,9 @@ public override void Project_single_scalar_value_subquery_in_query_with_optional base.Project_single_scalar_value_subquery_in_query_with_optional_navigation_works(); Assert.Equal( - @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] + @"@__p_0: 3 + +SELECT TOP(@__p_0) [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM [Orders] AS [o] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] ORDER BY [o].[OrderID], [o].[CustomerID] @@ -1193,11 +1195,5 @@ FROM [Order Details] AS [od0] "; private static string Sql => TestSqlLoggerFactory.Sql.Replace(Environment.NewLine, FileLineEnding); - - public QueryNavigationsSqlServerTest(NorthwindQuerySqlServerFixture fixture, ITestOutputHelper testOutputHelper) - : base(fixture) - { - //TestSqlLoggerFactory.CaptureOutput(testOutputHelper); - } } } diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs index d17a2fb800c..9214e9d6989 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs @@ -3243,6 +3243,21 @@ public override void GroupJoin_DefaultIfEmpty_Where() @"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] LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [o].[OrderID] IS NOT NULL AND (([o].[CustomerID] = N'ALFKI') AND [o].[CustomerID] IS NOT NULL) +ORDER BY [c].[CustomerID]", + Sql); + } + + public override void Join_GroupJoin_DefaultIfEmpty_Where() + { + base.Join_GroupJoin_DefaultIfEmpty_Where(); + + Assert.Equal( + @"SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region], [o2].[OrderID], [o2].[CustomerID], [o2].[EmployeeID], [o2].[OrderDate] +FROM [Customers] AS [c] +INNER JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +LEFT JOIN [Orders] AS [o2] ON [c].[CustomerID] = [o2].[CustomerID] +WHERE [o2].[OrderID] IS NOT NULL AND (([o2].[CustomerID] = N'ALFKI') AND [o2].[CustomerID] IS NOT NULL) ORDER BY [c].[CustomerID]", Sql); } @@ -5177,7 +5192,7 @@ public override void OfType_Select() base.OfType_Select(); Assert.Equal( - @"SELECT [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] + @"SELECT TOP(1) [t].[OrderID], [t].[CustomerID], [t].[EmployeeID], [t].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM ( SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] FROM [Orders] AS [o0] @@ -5192,7 +5207,7 @@ public override void OfType_Select_OfType_Select() base.OfType_Select_OfType_Select(); Assert.Equal( - @"SELECT [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] + @"SELECT TOP(1) [t1].[OrderID], [t1].[CustomerID], [t1].[EmployeeID], [t1].[OrderDate], [o.Customer].[CustomerID], [o.Customer].[Address], [o.Customer].[City], [o.Customer].[CompanyName], [o.Customer].[ContactName], [o.Customer].[ContactTitle], [o.Customer].[Country], [o.Customer].[Fax], [o.Customer].[Phone], [o.Customer].[PostalCode], [o.Customer].[Region] FROM ( SELECT [t0].[OrderID], [t0].[CustomerID], [t0].[EmployeeID], [t0].[OrderDate] FROM (