Skip to content

Commit

Permalink
Fix to #6937 - Query: Contains on element coming from optional naviga…
Browse files Browse the repository at this point in the history
…tion doesn't get server-evaluated

Problem was that when trying to translate contains ResultOperator we assumed that the translatable expression would always be member access or a EF.Property method call.
However in some cases (e.g. when item is part of an optional navigation) this is not the case, and the underlying member/EF.Property is wrapped around Convert and/or NullConditional.
Fix is to perform translation on the item before before checking what shape it is. This simplifies the code and protects from similar situations, because SqlTranslatingExpressionVisitor already optimizes out a lot of redundant nodes in the expression tree.
  • Loading branch information
maumar committed Nov 11, 2016
1 parent 214285d commit 409c9f8
Show file tree
Hide file tree
Showing 5 changed files with 53 additions and 24 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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() as AliasExpression;
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 });
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3094,6 +3094,26 @@ join l2 in ctx.LevelTwo on l1.Id equals ctx.LevelTwo.Select(l => l.Id).OrderBy(l
}
}

// Issue #6997
////[ConditionalFact]
public virtual void Complex_query_with_optional_navigations_and_client_side_evaluation()
{
AssertQuery<Level1>(
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<int>(
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<TResult>(object caller, Func<TResult> expression) where TResult : class
{
if (caller == null)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2385,6 +2385,15 @@ ORDER BY [l0].[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 = @"
";

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -398,15 +398,19 @@ 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]
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') 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);
}
Expand All @@ -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]
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
Expand All @@ -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);
}
Expand All @@ -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);
}
Expand Down

0 comments on commit 409c9f8

Please sign in to comment.