diff --git a/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs b/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs index 90ea27d11a1..83296f53143 100644 --- a/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs +++ b/src/Microsoft.EntityFrameworkCore.Relational/Query/ExpressionVisitors/SqlTranslatingExpressionVisitor.cs @@ -858,25 +858,10 @@ protected override Expression VisitSubQuery(SubQueryExpression expression) || fromExpression.NodeType == ExpressionType.ListInit || fromExpression.NodeType == ExpressionType.NewArrayInit) { - var memberItem = contains.Item as MemberExpression; - - if (memberItem != null) + var containsItem = Visit(contains.Item)?.RemoveConvert(); + if (containsItem != null) { - var aliasExpression = VisitMember(memberItem) as AliasExpression; - - return aliasExpression != null - ? new InExpression(aliasExpression, new[] { fromExpression }) - : null; - } - - var methodCallItem = contains.Item as MethodCallExpression; - - if (methodCallItem != null - && EntityQueryModelVisitor.IsPropertyMethod(methodCallItem.Method)) - { - var aliasExpression = (AliasExpression)VisitMethodCall(methodCallItem); - - return new InExpression(aliasExpression, new[] { fromExpression }); + return new InExpression(containsItem, new[] { fromExpression }); } } } diff --git a/src/Microsoft.EntityFrameworkCore.Specification.Tests/ComplexNavigationsQueryTestBase.cs b/src/Microsoft.EntityFrameworkCore.Specification.Tests/ComplexNavigationsQueryTestBase.cs index 7bbce4fed5a..995cb5d472a 100644 --- a/src/Microsoft.EntityFrameworkCore.Specification.Tests/ComplexNavigationsQueryTestBase.cs +++ b/src/Microsoft.EntityFrameworkCore.Specification.Tests/ComplexNavigationsQueryTestBase.cs @@ -3094,6 +3094,38 @@ join l2 in ctx.LevelTwo on l1.Id equals ctx.LevelTwo.Select(l => l.Id).OrderBy(l } } + [ConditionalFact] + public virtual void Contains_with_subquery_optional_navigation_and_constant_item() + { + AssertQuery( + l1s => l1s.Where(l1 => l1.OneToOne_Optional_FK.OneToMany_Optional.Distinct().Select(l3 => l3.Id).Contains(1)), + l1s => l1s.Where(l1 => MaybeScalar( + l1.OneToOne_Optional_FK, + () => l1.OneToOne_Optional_FK.OneToMany_Optional.Distinct().Select(l3 => l3.Id).Contains(1)) == true), + e => e.Id, + (e, a) => Assert.Equal(e.Id, a.Id)); + } + + // Issue #6997 + ////[ConditionalFact] + public virtual void Complex_query_with_optional_navigations_and_client_side_evaluation() + { + AssertQuery( + l1s => l1s.Where(l1 => !l1.OneToMany_Optional.Select(l2 => l2.OneToOne_Optional_FK.OneToOne_Optional_FK.Id).All(l4 => ClientMethod(l4))), + l1s => l1s.Where(l1 => l1.OneToMany_Optional.Select(l2 => MaybeScalar( + l2.OneToOne_Optional_FK, + () => MaybeScalar( + l2.OneToOne_Optional_FK.OneToOne_Optional_FK, + () => l2.OneToOne_Optional_FK.OneToOne_Optional_FK.Id))).All(a => true)), + e => e.Id, + (e, a) => Assert.Equal(e.Id, a.Id)); + } + + private bool ClientMethod(int? id) + { + return true; + } + private static TResult Maybe(object caller, Func expression) where TResult : class { if (caller == null) diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs index 68f58b75638..243c503813d 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/ComplexNavigationsQuerySqlServerTest.cs @@ -2385,6 +2385,35 @@ ORDER BY [l0].[Id] Sql); } + public override void Contains_with_subquery_optional_navigation_and_constant_item() + { + base.Contains_with_subquery_optional_navigation_and_constant_item(); + + 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_Optional_FK].[Id], [l1.OneToOne_Optional_FK].[Date], [l1.OneToOne_Optional_FK].[Level1_Optional_Id], [l1.OneToOne_Optional_FK].[Level1_Required_Id], [l1.OneToOne_Optional_FK].[Name], [l1.OneToOne_Optional_FK].[OneToMany_Optional_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Optional_Self_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_InverseId], [l1.OneToOne_Optional_FK].[OneToMany_Required_Self_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_PK_InverseId], [l1.OneToOne_Optional_FK].[OneToOne_Optional_SelfId] +FROM [Level1] AS [l1] +LEFT JOIN [Level2] AS [l1.OneToOne_Optional_FK] ON [l1].[Id] = [l1.OneToOne_Optional_FK].[Level1_Optional_Id] +WHERE 1 IN ( + SELECT [t].[Id] + FROM ( + SELECT DISTINCT [l0].* + FROM [Level3] AS [l0] + WHERE [l1.OneToOne_Optional_FK].[Id] = [l0].[OneToMany_Optional_InverseId] + ) AS [t] +) +ORDER BY [l1].[Id]", + Sql); + } + + public override void Complex_query_with_optional_navigations_and_client_side_evaluation() + { + base.Complex_query_with_optional_navigations_and_client_side_evaluation(); + + Assert.Equal( + @"", + Sql); + } + private const string FileLineEnding = @" "; diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs index 46a9d093f2d..b25df1ab5fa 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/GearsOfWarQuerySqlServerTest.cs @@ -398,7 +398,7 @@ public override void Include_where_list_contains_navigation() { base.Include_where_list_contains_navigation(); - Assert.Equal( + Assert.StartsWith( @"SELECT [t].[Id] FROM [CogTag] AS [t] @@ -406,7 +406,11 @@ FROM [CogTag] AS [t] 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') AND [g.Tag].[Id] IS NOT NULL +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g.Tag].[Id] IS NOT NULL AND [g.Tag].[Id] IN ('", + Sql); + + Assert.EndsWith( + @"')) ORDER BY [g].[Nickname], [g].[SquadId]", Sql); } @@ -415,7 +419,7 @@ public override void Include_where_list_contains_navigation2() { base.Include_where_list_contains_navigation2(); - Assert.Equal( + Assert.StartsWith( @"SELECT [t].[Id] FROM [CogTag] AS [t] @@ -424,7 +428,11 @@ FROM [Gear] AS [g] LEFT JOIN [CogTag] AS [g.Tag] ON ([g].[Nickname] = [g.Tag].[GearNickName]) AND ([g].[SquadId] = [g.Tag].[GearSquadId]) INNER JOIN [City] AS [g.CityOfBirth] ON [g].[CityOrBirthName] = [g.CityOfBirth].[Name] LEFT JOIN [CogTag] AS [c] ON ([c].[GearNickName] = [g].[Nickname]) AND ([c].[GearSquadId] = [g].[SquadId]) -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND [g.CityOfBirth].[Location] IS NOT NULL +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g.CityOfBirth].[Location] IS NOT NULL AND [g.Tag].[Id] IN ('", + Sql); + + Assert.EndsWith( + @"')) ORDER BY [g].[Nickname], [g].[SquadId]", Sql); } @@ -433,14 +441,18 @@ public override void Navigation_accessed_twice_outside_and_inside_subquery() { base.Navigation_accessed_twice_outside_and_inside_subquery(); - Assert.Equal( + Assert.StartsWith( @"SELECT [t].[Id] FROM [CogTag] AS [t] 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] FROM [Gear] AS [g] LEFT JOIN [CogTag] AS [g.Tag] ON ([g].[Nickname] = [g.Tag].[GearNickName]) AND ([g].[SquadId] = [g.Tag].[GearSquadId]) -WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND [g.Tag].[Id] IS NOT NULL +WHERE [g].[Discriminator] IN (N'Officer', N'Gear') AND ([g.Tag].[Id] IS NOT NULL AND [g.Tag].[Id] IN ('", + Sql); + + Assert.EndsWith( + @"')) ORDER BY [g].[Nickname], [g].[SquadId]", Sql); } diff --git a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs index 7c8e8fea6d6..cd8eae68f6c 100644 --- a/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs +++ b/test/Microsoft.EntityFrameworkCore.SqlServer.FunctionalTests/QueryNavigationsSqlServerTest.cs @@ -910,6 +910,7 @@ public override void Navigation_inside_contains() @"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] IN (N'Novigrad', N'Seattle') ORDER BY [o].[CustomerID]", Sql); } @@ -923,6 +924,7 @@ public override void Navigation_inside_contains_nested() 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] IN (N'Novigrad', N'Seattle') ORDER BY [od.Order].[CustomerID]", Sql); } @@ -936,6 +938,7 @@ public override void Navigation_from_join_clause_inside_contains() FROM [Order Details] AS [od] INNER JOIN [Orders] AS [o] ON [od].[OrderID] = [o].[OrderID] LEFT JOIN [Customers] AS [o.Customer] ON [o].[CustomerID] = [o.Customer].[CustomerID] +WHERE [o.Customer].[Country] IN (N'USA', N'Redania') ORDER BY [o].[CustomerID]", Sql); }