diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs index c5461fb0bfc..4d57e18f329 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/Expressions/SelectExpression.cs @@ -241,7 +241,7 @@ public virtual TableExpressionBase GetTableForQuerySource([NotNull] IQuerySource return _tables.FirstOrDefault(te => te.QuerySource == querySource || ((te as SelectExpression)?.HandlesQuerySource(querySource) ?? false)) - ?? _tables.Single(); + ?? _tables.Last(); } /// @@ -816,7 +816,15 @@ public virtual void PrependToOrderBy([NotNull] IEnumerable orderings) { Check.NotNull(orderings, nameof(orderings)); - _orderBy.InsertRange(0, orderings); + var oldOrderBy = _orderBy.ToList(); + + _orderBy.Clear(); + _orderBy.AddRange(orderings); + + foreach (var ordering in oldOrderBy) + { + AddToOrderBy(ordering); + } } /// diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs index 20bf5893120..1d6cf1c9c5e 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/RelationalQueryModelVisitor.cs @@ -415,6 +415,27 @@ public override void VisitAdditionalFromClause( Check.NotNull(queryModel, nameof(queryModel)); base.VisitAdditionalFromClause(fromClause, queryModel, index); + + var fromQuerySourceReferenceExpression + = fromClause.FromExpression as QuerySourceReferenceExpression; + + if (fromQuerySourceReferenceExpression != null) + { + var previousQuerySource = FindPreviousQuerySource(queryModel, index - 1); + + if (previousQuerySource != null + && !RequiresClientJoin) + { + var previousSelectExpression = TryGetQuery(previousQuerySource); + + if (previousSelectExpression != null) + { + AddQuery(fromClause, previousSelectExpression); + } + } + + return; + } RequiresClientSelectMany = true; @@ -581,7 +602,7 @@ protected virtual void OptimizeJoinClause( Check.NotNull(operatorToFlatten, nameof(operatorToFlatten)); RequiresClientJoin = true; - + var previousQuerySource = FindPreviousQuerySource(queryModel, index); var previousSelectExpression @@ -593,8 +614,9 @@ var previousSelectProjectionCount = previousSelectExpression?.Projection.Count ?? -1; baseVisitAction(); - - if (previousSelectExpression != null) + + if (!RequiresClientSelectMany + && previousSelectExpression != null) { var selectExpression = TryGetQuery(joinClause); diff --git a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryNavigationsTestBase.cs b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryNavigationsTestBase.cs index 5785ecebdff..0eb10ff3aca 100644 --- a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryNavigationsTestBase.cs +++ b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryNavigationsTestBase.cs @@ -9,8 +9,13 @@ using Microsoft.EntityFrameworkCore.Specification.Tests.TestUtilities.Xunit; using Xunit; +// ReSharper disable InconsistentNaming +// ReSharper disable PossibleMultipleEnumeration +// ReSharper disable ReplaceWithSingleCallToFirstOrDefault +// ReSharper disable ReplaceWithSingleCallToAny +// ReSharper disable ReplaceWithSingleCallToFirst +// ReSharper disable StringStartsWithIsCultureSpecific // ReSharper disable UseCollectionCountProperty - // ReSharper disable AccessToDisposedClosure // ReSharper disable PossibleUnintendedReferenceComparison @@ -43,8 +48,7 @@ var orders } } - // issue 4539 - ////[ConditionalFact] + [ConditionalFact] public virtual void Select_Where_Navigation_Scalar_Equals_Navigation_Scalar() { using (var context = CreateContext()) @@ -59,8 +63,7 @@ from o2 in context.Set().Where(o => o.OrderID < 10400) } } - // issue 4539 - ////[ConditionalFact] + [ConditionalFact] public virtual void Select_Where_Navigation_Scalar_Equals_Navigation_Scalar_Projected() { using (var context = CreateContext()) @@ -186,8 +189,7 @@ public virtual void Select_Where_Navigation_Null_Deep() entryCount: 6); } - // issue 4539 - ////[ConditionalFact] + [ConditionalFact] public virtual void Select_Where_Navigation_Equals_Navigation() { using (var context = CreateContext()) @@ -616,11 +618,11 @@ public virtual void Collection_select_nav_prop_first_or_default_then_nav_prop_ne ); } - // #5427 - ////[ConditionalFact] + [ConditionalFact] public virtual void Collection_select_nav_prop_first_or_default_then_nav_prop_nested_with_orderby() { AssertQuery( + // ReSharper disable once StringStartsWithIsCultureSpecific (cs, os) => cs.Where(e => e.CustomerID.StartsWith("A")) .Select(c => os.OrderBy(o => o.CustomerID).FirstOrDefault(o =>o.CustomerID == "ALFKI").Customer.City)); } @@ -728,13 +730,10 @@ where c.Orders.Select(o => o.OrderID) entryCount: 1); } - private int ClientMethod(int argument) - { - return argument; - } + // ReSharper disable once MemberCanBeMadeStatic.Local + private int ClientMethod(int argument) => argument; - // issue #4547 - ////[ConditionalFact] + [ConditionalFact] public virtual void Navigation_in_subquery_referencing_outer_query() { using (var context = CreateContext()) @@ -794,15 +793,13 @@ join efItem in efItems on l2oItem.Key equals efItem.Key }); } - // issue #3676 - //// [ConditionalFact] + [ConditionalFact] public virtual void Let_group_by_nav_prop() { AssertQuery>( ods => from od in ods let customer = od.Order.CustomerID - group od by customer - into odg + group od by customer into odg select odg, asserter: (l2oItems, efItems) => { @@ -836,10 +833,8 @@ protected void AssertQuery( bool assertOrder = false, int entryCount = 0, Action, IList> asserter = null) - where TItem : class - { - AssertQuery(query, query, assertOrder, entryCount, asserter); - } + where TItem : class + => AssertQuery(query, query, assertOrder, entryCount, asserter); protected void AssertQuery( Func, IQueryable, IQueryable> query, @@ -847,21 +842,16 @@ protected void AssertQuery( int entryCount = 0, Action, IList> asserter = null) where TItem1 : class - where TItem2 : class - { - AssertQuery(query, query, assertOrder, entryCount, asserter); - } - + where TItem2 : class + => AssertQuery(query, query, assertOrder, entryCount, asserter); protected void AssertQuery( Func, IQueryable> query, bool assertOrder = false, int entryCount = 0, Action, IList> asserter = null) - where TItem : class - { - AssertQuery(query, query, assertOrder, entryCount, asserter); - } + where TItem : class + => AssertQuery(query, query, assertOrder, entryCount, asserter); protected void AssertQuery( Func, IQueryable> efQuery, diff --git a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs index fd113600e32..8a80329d729 100644 --- a/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs +++ b/src/Microsoft.EntityFrameworkCore.Specification.Tests/QueryTestBase.cs @@ -3153,7 +3153,7 @@ from c in cs select new { c, hasOrders }); } - // TODO: Need to figure out how to do this + // TODO: Need to figure out how to do this // [ConditionalFact] // public virtual void GroupBy_anonymous() // { @@ -4623,6 +4623,30 @@ from o in orders.DefaultIfEmpty() select o); } + + [ConditionalFact] + public virtual void GroupJoin_DefaultIfEmpty_Where() + { + AssertQuery((cs, os) => + from c in cs + join o in os on c.CustomerID equals o.CustomerID into orders + from o in orders + where o.CustomerID == "ALFKI" + select o); + } + + [ConditionalFact] + public virtual void GroupJoin_DefaultIfEmpty_Where_OrderBy() + { + AssertQuery((cs, os) => + from c in cs + join o in os on c.CustomerID equals o.CustomerID into orders + from o in orders + where o.CustomerID == "ALFKI" || c.CustomerID == "ANATR" + orderby c.City + select o); + } + [ConditionalFact] public virtual void SelectMany_Joined() { diff --git a/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs b/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs index 4a89081a7e3..5ccb52075ca 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/EntityQueryModelVisitor.cs @@ -547,9 +547,15 @@ var lastTrackingModifier return; } + var groupResultOperator + = queryModel.ResultOperators.OfType().LastOrDefault(); + + var outputExpression + = groupResultOperator?.ElementSelector ?? queryModel.SelectClause.Selector; + var entityTrackingInfos = _entityResultFindingExpressionVisitorFactory.Create(QueryCompilationContext) - .FindEntitiesInResult(queryModel.SelectClause.Selector); + .FindEntitiesInResult(outputExpression); if (entityTrackingInfos.Any()) { @@ -561,13 +567,13 @@ var entityTrackingInfos if (resultItemTypeInfo.IsGenericType && (resultItemTypeInfo.GetGenericTypeDefinition() == typeof(IGrouping<,>) || resultItemTypeInfo.GetGenericTypeDefinition() == typeof(IAsyncGrouping<,>))) + { trackingMethod = LinqOperatorProvider.TrackGroupedEntities .MakeGenericMethod( resultItemType.GenericTypeArguments[0], - resultItemType.GenericTypeArguments[1], - queryModel.SelectClause.Selector.Type); + resultItemType.GenericTypeArguments[1]); } else { @@ -575,7 +581,7 @@ var entityTrackingInfos = LinqOperatorProvider.TrackEntities .MakeGenericMethod( resultItemType, - queryModel.SelectClause.Selector.Type); + outputExpression.Type); } _expression @@ -586,13 +592,13 @@ var entityTrackingInfos Expression.Constant(entityTrackingInfos), Expression.Constant( _getEntityAccessors - .MakeGenericMethod(queryModel.SelectClause.Selector.Type) + .MakeGenericMethod(outputExpression.Type) .Invoke( null, new object[] { entityTrackingInfos, - queryModel.SelectClause.Selector + outputExpression }))); } } diff --git a/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/NavigationRewritingExpressionVisitor.cs b/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/NavigationRewritingExpressionVisitor.cs index 5c500378f33..992f9bac2c1 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/NavigationRewritingExpressionVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/ExpressionVisitors/Internal/NavigationRewritingExpressionVisitor.cs @@ -52,7 +52,6 @@ public NavigationJoin( INavigation navigation, JoinClause joinClause, IEnumerable additionalBodyClauses, - bool optionalNavigationInChain, bool dependentToPrincipal, QuerySourceReferenceExpression querySourceReferenceExpression) : this( @@ -61,7 +60,6 @@ public NavigationJoin( joinClause, null, additionalBodyClauses, - optionalNavigationInChain, dependentToPrincipal, querySourceReferenceExpression) { @@ -72,7 +70,6 @@ public NavigationJoin( INavigation navigation, GroupJoinClause groupJoinClause, IEnumerable additionalBodyClauses, - bool optionalNavigationInChain, bool dependentToPrincipal, QuerySourceReferenceExpression querySourceReferenceExpression) : this( @@ -81,7 +78,6 @@ public NavigationJoin( null, groupJoinClause, additionalBodyClauses, - optionalNavigationInChain, dependentToPrincipal, querySourceReferenceExpression) { @@ -93,7 +89,6 @@ private NavigationJoin( JoinClause joinClause, GroupJoinClause groupJoinClause, IEnumerable additionalBodyClauses, - bool optionalNavigationInChain, bool dependentToPrincipal, QuerySourceReferenceExpression querySourceReferenceExpression) { @@ -102,7 +97,6 @@ private NavigationJoin( JoinClause = joinClause; GroupJoinClause = groupJoinClause; AdditionalBodyClauses = additionalBodyClauses; - OptionalNavigationInChain = optionalNavigationInChain; DependentToPrincipal = dependentToPrincipal; QuerySourceReferenceExpression = querySourceReferenceExpression; } @@ -112,7 +106,6 @@ private NavigationJoin( public JoinClause JoinClause { get; } public GroupJoinClause GroupJoinClause { get; } public IEnumerable AdditionalBodyClauses { get; } - public bool OptionalNavigationInChain { get; } public bool DependentToPrincipal { get; } public QuerySourceReferenceExpression QuerySourceReferenceExpression { get; } public readonly List NavigationJoins = new List(); @@ -422,7 +415,7 @@ protected override Expression VisitMethodCall(MethodCallExpression node) ps.ToList(), qs, node.Arguments[0], - (string)(node.Arguments[1] as ConstantExpression).Value, + (string)((ConstantExpression)node.Arguments[1]).Value, node.Type, e => Expression.Call(node.Method, e, node.Arguments[1])); }) @@ -691,7 +684,6 @@ var groupJoinClause navigation, groupJoinClause, additionalBodyClauses, - optionalNavigationInChain, navigation.IsDependentToPrincipal(), new QuerySourceReferenceExpression(defaultIfEmptyAdditionalFromClause))); } @@ -704,7 +696,6 @@ var groupJoinClause navigation, joinClause, additionalBodyClauses, - optionalNavigationInChain, navigation.IsDependentToPrincipal(), innerQuerySourceReferenceExpression)); } @@ -721,7 +712,7 @@ var groupJoinClause if (optionalNavigationInChain) { - Expression memberAccessExpression = propertyCreator(querySourceReferenceExpression); + var memberAccessExpression = propertyCreator(querySourceReferenceExpression); if (!propertyType.IsNullableType()) { memberAccessExpression = Expression.Convert(memberAccessExpression, propertyType.MakeNullable()); @@ -1055,7 +1046,7 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer { Func expressionExtractor = o => o.Predicate; Action adjuster = (o, e) => o.Predicate = e; - VisitAndAdjustResultOperatorType(allResultOperator, expressionExtractor, adjuster, queryModel, index); + VisitAndAdjustResultOperatorType(allResultOperator, expressionExtractor, adjuster); return; } @@ -1065,7 +1056,7 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer { Func expressionExtractor = o => o.Item; Action adjuster = (o, e) => o.Item = e; - VisitAndAdjustResultOperatorType(containsResultOperator, expressionExtractor, adjuster, queryModel, index); + VisitAndAdjustResultOperatorType(containsResultOperator, expressionExtractor, adjuster); return; } @@ -1075,7 +1066,7 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer { Func expressionExtractor = o => o.Count; Action adjuster = (o, e) => o.Count = e; - VisitAndAdjustResultOperatorType(skipResultOperator, expressionExtractor, adjuster, queryModel, index); + VisitAndAdjustResultOperatorType(skipResultOperator, expressionExtractor, adjuster); return; } @@ -1085,7 +1076,7 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer { Func expressionExtractor = o => o.Count; Action adjuster = (o, e) => o.Count = e; - VisitAndAdjustResultOperatorType(takeResultOperator, expressionExtractor, adjuster, queryModel, index); + VisitAndAdjustResultOperatorType(takeResultOperator, expressionExtractor, adjuster); return; } @@ -1126,9 +1117,7 @@ public override void VisitResultOperator(ResultOperatorBase resultOperator, Quer private void VisitAndAdjustResultOperatorType( TResultOperator resultOperator, Func expressionExtractor, - Action adjuster, - QueryModel queryModel, - int index) + Action adjuster) where TResultOperator : ResultOperatorBase { var originalExpression = expressionExtractor(resultOperator); diff --git a/src/Microsoft.EntityFrameworkCore/Query/Internal/AsyncLinqOperatorProvider.cs b/src/Microsoft.EntityFrameworkCore/Query/Internal/AsyncLinqOperatorProvider.cs index 5021fa0d922..a8a55225440 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/Internal/AsyncLinqOperatorProvider.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/Internal/AsyncLinqOperatorProvider.cs @@ -231,16 +231,15 @@ private static readonly MethodInfo _trackGroupedEntities [UsedImplicitly] // ReSharper disable once InconsistentNaming - private static IAsyncEnumerable> _TrackGroupedEntities( - IAsyncEnumerable> groupings, + private static IAsyncEnumerable> _TrackGroupedEntities( + IAsyncEnumerable> groupings, QueryContext queryContext, IList entityTrackingInfos, - IList> entityAccessors) - where TIn : class + IList> entityAccessors) { return _Select( groupings, - g => new TrackingGrouping( + g => new TrackingGrouping( g, queryContext, entityTrackingInfos, @@ -253,19 +252,18 @@ private static IAsyncEnumerable> _TrackGrouped /// public virtual MethodInfo TrackGroupedEntities => _trackGroupedEntities; - internal class TrackingGrouping : IGrouping - where TIn : class + internal class TrackingGrouping : IGrouping { - private readonly IGrouping _grouping; + private readonly IGrouping _grouping; private readonly QueryContext _queryContext; private readonly IList _entityTrackingInfos; - private readonly IList> _entityAccessors; + private readonly IList> _entityAccessors; public TrackingGrouping( - IGrouping grouping, + IGrouping grouping, QueryContext queryContext, IList entityTrackingInfos, - IList> entityAccessors) + IList> entityAccessors) { _grouping = grouping; _queryContext = queryContext; @@ -275,7 +273,7 @@ public TrackingGrouping( public TKey Key => _grouping.Key; - IEnumerator IEnumerable.GetEnumerator() + IEnumerator IEnumerable.GetEnumerator() { _queryContext.BeginTrackingQuery(); @@ -285,7 +283,7 @@ IEnumerator IEnumerable.GetEnumerator() { for (var i = 0; i < _entityTrackingInfos.Count; i++) { - var entity = _entityAccessors[i](result as TIn); + var entity = _entityAccessors[i](result); if (entity != null) { @@ -298,7 +296,7 @@ IEnumerator IEnumerable.GetEnumerator() } } - IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this).GetEnumerator(); + IEnumerator IEnumerable.GetEnumerator() => ((IEnumerable)this).GetEnumerator(); } private static readonly MethodInfo _toSequence diff --git a/src/Microsoft.EntityFrameworkCore/Query/Internal/LinqOperatorProvider.cs b/src/Microsoft.EntityFrameworkCore/Query/Internal/LinqOperatorProvider.cs index df7106d1873..4ed5143f3f6 100644 --- a/src/Microsoft.EntityFrameworkCore/Query/Internal/LinqOperatorProvider.cs +++ b/src/Microsoft.EntityFrameworkCore/Query/Internal/LinqOperatorProvider.cs @@ -209,16 +209,15 @@ private static readonly MethodInfo _trackGroupedEntities [UsedImplicitly] // ReSharper disable once InconsistentNaming - private static IEnumerable> _TrackGroupedEntities( - IEnumerable> groupings, + private static IEnumerable> _TrackGroupedEntities( + IEnumerable> groupings, QueryContext queryContext, IList entityTrackingInfos, - IList> entityAccessors) - where TIn : class + IList> entityAccessors) { return groupings .Select(g => - new TrackingGrouping( + new TrackingGrouping( g, queryContext, entityTrackingInfos, @@ -231,19 +230,18 @@ private static IEnumerable> _TrackGroupedEntities public virtual MethodInfo TrackGroupedEntities => _trackGroupedEntities; - private class TrackingGrouping : IGrouping - where TIn : class + private class TrackingGrouping : IGrouping { - private readonly IGrouping _grouping; + private readonly IGrouping _grouping; private readonly QueryContext _queryContext; private readonly IList _entityTrackingInfos; - private readonly IList> _entityAccessors; + private readonly IList> _entityAccessors; public TrackingGrouping( - IGrouping grouping, + IGrouping grouping, QueryContext queryContext, IList entityTrackingInfos, - IList> entityAccessors) + IList> entityAccessors) { _grouping = grouping; _queryContext = queryContext; @@ -253,7 +251,7 @@ public TrackingGrouping( public TKey Key => _grouping.Key; - public IEnumerator GetEnumerator() + public IEnumerator GetEnumerator() { _queryContext.BeginTrackingQuery(); @@ -263,7 +261,7 @@ public IEnumerator GetEnumerator() { for (var i = 0; i < _entityTrackingInfos.Count; i++) { - var entity = _entityAccessors[i](result as TIn); + var entity = _entityAccessors[i](result); if (entity != null) { diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs index c6206ee861a..fecb064c107 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs @@ -277,7 +277,17 @@ public override void Select_Where_Navigation_Scalar_Equals_Navigation_Scalar() base.Select_Where_Navigation_Scalar_Equals_Navigation_Scalar(); Assert.StartsWith( - @"", + @"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].[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].[OrderID] < 10300 +ORDER BY [o].[CustomerID] + +SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o0]", Sql); } @@ -286,7 +296,17 @@ public override void Select_Where_Navigation_Scalar_Equals_Navigation_Scalar_Pro base.Select_Where_Navigation_Scalar_Equals_Navigation_Scalar_Projected(); Assert.StartsWith( - @"", + @"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].[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].[OrderID] < 10300 +ORDER BY [o].[CustomerID] + +SELECT [o0].[OrderID], [o0].[CustomerID], [o0].[EmployeeID], [o0].[OrderDate] +FROM [Orders] AS [o0]", Sql); } @@ -762,8 +782,16 @@ public override void Collection_select_nav_prop_first_or_default_then_nav_prop_n { base.Collection_select_nav_prop_first_or_default_then_nav_prop_nested_with_orderby(); - Assert.Equal( - @"", + Assert.StartsWith( + @"SELECT 1 +FROM [Customers] AS [e] +WHERE [e].[CustomerID] LIKE N'A' + N'%' + +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].[CustomerID] = N'ALFKI' +ORDER BY [o].[CustomerID]", Sql); } @@ -884,17 +912,17 @@ public override void Navigation_in_subquery_referencing_outer_query() { base.Navigation_in_subquery_referencing_outer_query(); - Assert.Equal( - @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate], [o.Customer].[Country] + Assert.StartsWith( + @"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] -INNER JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] -WHERE ( - SELECT COUNT(*) - FROM [Order Details] AS [od] - INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] - INNER 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", +LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +ORDER BY [o].[CustomerID] + +SELECT [od.Order0].[OrderID], [od.Order0].[CustomerID], [od.Order0].[EmployeeID], [od.Order0].[OrderDate], [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]", Sql); } @@ -928,7 +956,10 @@ public override void Let_group_by_nav_prop() base.Let_group_by_nav_prop(); Assert.Equal( - @"", + @"SELECT [od].[OrderID], [od].[ProductID], [od].[Discount], [od].[Quantity], [od].[UnitPrice], [od.Order].[CustomerID] +FROM [Order Details] AS [od] +INNER JOIN [Orders] AS [od.Order] ON [od].[OrderID] = [od.Order].[OrderID] +ORDER BY [od.Order].[CustomerID]", Sql); } diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs index 683c9bcb20e..f7335c256cb 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QuerySqlServerTest.cs @@ -2974,6 +2974,32 @@ FROM [Customers] AS [c0] Sql); } + public override void GroupJoin_DefaultIfEmpty_Where() + { + base.GroupJoin_DefaultIfEmpty_Where(); + + Assert.Equal( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE [o].[CustomerID] = N'ALFKI' +ORDER BY [c].[CustomerID]", + Sql); + } + + public override void GroupJoin_DefaultIfEmpty_Where_OrderBy() + { + base.GroupJoin_DefaultIfEmpty_Where_OrderBy(); + + Assert.Equal( + @"SELECT [o].[OrderID], [o].[CustomerID], [o].[EmployeeID], [o].[OrderDate] +FROM [Customers] AS [c] +LEFT JOIN [Orders] AS [o] ON [c].[CustomerID] = [o].[CustomerID] +WHERE ([o].[CustomerID] = N'ALFKI') OR ([c].[CustomerID] = N'ANATR') +ORDER BY [c].[City], [c].[CustomerID]", + Sql); + } + public override void GroupJoin_simple() { base.GroupJoin_simple(); @@ -5392,7 +5418,7 @@ public override void Environment_newline_is_funcletized() base.Environment_newline_is_funcletized(); Assert.Equal( - @"@__NewLine_0: + @"@__NewLine_0: (Size = 450) SELECT [c].[CustomerID], [c].[Address], [c].[City], [c].[CompanyName], [c].[ContactName], [c].[ContactTitle], [c].[Country], [c].[Fax], [c].[Phone], [c].[PostalCode], [c].[Region]