Skip to content

Commit

Permalink
Partial fix to #3103 - Must be reducible node exception when performi…
Browse files Browse the repository at this point in the history
…ng Join

Problem is in a query with join, that has a navigation property in inner key selector.
We normally try to expand the navigation property into a join clause, however if done for inner key selector, leads to chicken-and-egg problem:

e.g.
from p in ctx.Payouts
join a in ctx.Assets on p.Id equals a.Good.Id
select p

translated to:
from p in ctx.Payouts
join g in ctx.Good on a.Id equals g.Id // invalid, because a was not declared yet
join a in ctx.Assets on g.Id equals a.Id

(alternatively)

from p in ctx.Payouts
join a in ctx.Assets on g.Id equals a.Id // invalid, because g was not declared yet
join g in ctx.Good on a.Id equals g.Id

Is some cases this can we worked around by translating navigation property into a FK:
from p in ctx.Payouts
join a in ctx.Assets on p.Id equals a.GoodId
select p

This is only possible if we actually have foreign key on this side of the navigation, and if the navigation itself is simple (not nested, not composite and we are accessing PK after the navigation).
  • Loading branch information
maumar committed Oct 9, 2015
1 parent e6e9449 commit 4db95a1
Show file tree
Hide file tree
Showing 3 changed files with 85 additions and 0 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
Expand Down Expand Up @@ -236,6 +237,15 @@ protected override Expression VisitMember(MemberExpression memberExpression)

if (navigations.Any())
{
if (navigations.Count == 1 && navigations[0].PointsToPrincipal())
{
var foreignKeyMemberAccess = CreateForeignKeyMemberAccess(memberExpression, navigations[0]);
if (foreignKeyMemberAccess != null)
{
return foreignKeyMemberAccess;
}
}

var outerQuerySourceReferenceExpression = new QuerySourceReferenceExpression(qs);

var innerQuerySourceReferenceExpression
Expand All @@ -251,6 +261,29 @@ var innerQuerySourceReferenceExpression
?? base.VisitMember(memberExpression);
}

private Expression CreateForeignKeyMemberAccess(MemberExpression memberExpression, INavigation navigation)
{
var principalKey = navigation.ForeignKey.PrincipalKey;
if (principalKey.Properties.Count == 1)
{
var principalKeyProperty = principalKey.Properties[0];
if (principalKeyProperty.Name == memberExpression.Member.Name)
{
Debug.Assert(navigation.ForeignKey.Properties.Count == 1);

var foreignKeyProperty = navigation.ForeignKey.Properties[0];
var declaringExpression = ((MemberExpression)memberExpression.Expression).Expression;
var foreignKeyPropertyExpression = CreateKeyAccessExpression(declaringExpression, navigation.ForeignKey.Properties);

return foreignKeyPropertyExpression.Type != principalKeyProperty.ClrType
? Expression.Convert(foreignKeyPropertyExpression, principalKeyProperty.ClrType)
: foreignKeyPropertyExpression;
}
}

return null;
}

private Expression CreateJoinsForNavigations(
QuerySourceReferenceExpression outerQuerySourceReferenceExpression,
IEnumerable<INavigation> navigations)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -228,5 +228,35 @@ public virtual void Multi_level_include_with_short_circuiting()
}
}
}

[Fact]
public virtual void Join_navigation_on_inner_selector_translated_to_FK()
{
using (var context = CreateContext())
{
var query = from e1 in context.LevelOne
join e2 in context.LevelTwo on e1.Id equals e2.OneToOne_Optional_PK_Inverse.Id
select new { Id1 = e1.Id, Id2 = e2.Id };

var result = query.ToList();

Assert.Equal(5, result.Count);
}
}

[Fact]
public virtual void Join_navigation_on_outer_selector_translated_to_FK()
{
using (var context = CreateContext())
{
var query = from e2 in context.LevelTwo
join e1 in context.LevelOne on e2.OneToOne_Optional_PK_Inverse.Id equals e1.Id
select new { Id2 = e2.Id, Id1 = e1.Id };

var result = query.ToList();

Assert.Equal(5, result.Count);
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,28 @@ FROM [ComplexNavigationField] AS [x]
Sql);
}

public override void Join_navigation_on_inner_selector_translated_to_FK()
{
base.Join_navigation_on_inner_selector_translated_to_FK();

Assert.Equal(
@"SELECT [e1].[Id], [e2].[Id]
FROM [Level1] AS [e1]
INNER JOIN [Level2] AS [e2] ON [e1].[Id] = [e2].[OneToOne_Optional_PK_InverseId]",
Sql);
}

public override void Join_navigation_on_outer_selector_translated_to_FK()
{
base.Join_navigation_on_outer_selector_translated_to_FK();

Assert.Equal(
@"SELECT [e2].[Id], [e1].[Id]
FROM [Level2] AS [e2]
INNER JOIN [Level1] AS [e1] ON [e2].[OneToOne_Optional_PK_InverseId] = [e1].[Id]",
Sql);
}

private static string Sql => TestSqlLoggerFactory.Sql;
}
}

0 comments on commit 4db95a1

Please sign in to comment.