Skip to content

Commit a1e709b

Browse files
maca88bahusoid
andauthored
Fix Linq Fetch over component after fetching an association (#2517)
Fixes #2511 Co-authored-by: Roman Artiukhin <[email protected]>
1 parent 98de5ae commit a1e709b

File tree

4 files changed

+103
-7
lines changed

4 files changed

+103
-7
lines changed

src/NHibernate.Test/Async/FetchLazyProperties/FetchLazyPropertiesFixture.cs

+34
Original file line numberDiff line numberDiff line change
@@ -666,6 +666,40 @@ private static void AssertFetchComponentManyToOne(Person person)
666666

667667
#endregion
668668

669+
#region TestHqlFetchManyToOneAndComponentManyToOne
670+
671+
[Test]
672+
public async Task TestHqlFetchManyToOneAndComponentManyToOneAsync()
673+
{
674+
Person person;
675+
using (var s = OpenSession())
676+
{
677+
person = await (s.CreateQuery("from Person p fetch p.Address left join fetch p.Address.Continent left join fetch p.BestFriend where p.Id = 1").UniqueResultAsync<Person>());
678+
}
679+
680+
AssertFetchManyToOneAndComponentManyToOne(person);
681+
}
682+
683+
[Test]
684+
public async Task TestLinqFetchManyToOneAndComponentManyToOneAsync()
685+
{
686+
Person person;
687+
using (var s = OpenSession())
688+
{
689+
person = await (s.Query<Person>().Fetch(o => o.BestFriend).Fetch(o => o.Address).ThenFetch(o => o.Continent).FirstOrDefaultAsync(o => o.Id == 1));
690+
}
691+
692+
AssertFetchManyToOneAndComponentManyToOne(person);
693+
}
694+
695+
private static void AssertFetchManyToOneAndComponentManyToOne(Person person)
696+
{
697+
AssertFetchComponentManyToOne(person);
698+
Assert.That(NHibernateUtil.IsInitialized(person.BestFriend), Is.True);
699+
}
700+
701+
#endregion
702+
669703
#region FetchSubClassFormula
670704

671705
[Test]

src/NHibernate.Test/FetchLazyProperties/FetchLazyPropertiesFixture.cs

+34
Original file line numberDiff line numberDiff line change
@@ -655,6 +655,40 @@ private static void AssertFetchComponentManyToOne(Person person)
655655

656656
#endregion
657657

658+
#region TestHqlFetchManyToOneAndComponentManyToOne
659+
660+
[Test]
661+
public void TestHqlFetchManyToOneAndComponentManyToOne()
662+
{
663+
Person person;
664+
using (var s = OpenSession())
665+
{
666+
person = s.CreateQuery("from Person p fetch p.Address left join fetch p.Address.Continent left join fetch p.BestFriend where p.Id = 1").UniqueResult<Person>();
667+
}
668+
669+
AssertFetchManyToOneAndComponentManyToOne(person);
670+
}
671+
672+
[Test]
673+
public void TestLinqFetchManyToOneAndComponentManyToOne()
674+
{
675+
Person person;
676+
using (var s = OpenSession())
677+
{
678+
person = s.Query<Person>().Fetch(o => o.BestFriend).Fetch(o => o.Address).ThenFetch(o => o.Continent).FirstOrDefault(o => o.Id == 1);
679+
}
680+
681+
AssertFetchManyToOneAndComponentManyToOne(person);
682+
}
683+
684+
private static void AssertFetchManyToOneAndComponentManyToOne(Person person)
685+
{
686+
AssertFetchComponentManyToOne(person);
687+
Assert.That(NHibernateUtil.IsInitialized(person.BestFriend), Is.True);
688+
}
689+
690+
#endregion
691+
658692
#region FetchSubClassFormula
659693

660694
[Test]

src/NHibernate/Linq/IntermediateHqlTree.cs

+5
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ public void AddFromClause(HqlTreeNode from)
127127
_root.NodesPreOrder.OfType<HqlFrom>().First().AddChild(from);
128128
}
129129

130+
internal HqlRange GetFromRangeClause()
131+
{
132+
return _root.NodesPreOrder.OfType<HqlFrom>().First().Children.OfType<HqlRange>().FirstOrDefault();
133+
}
134+
130135
public void AddSelectClause(HqlTreeNode select)
131136
{
132137
_root.NodesPreOrder.OfType<HqlSelectFrom>().First().AddChild(select);

src/NHibernate/Linq/Visitors/ResultOperatorProcessors/ProcessFetch.cs

+30-7
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,31 @@ public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMode
1919
}
2020

2121
public void Process(FetchRequestBase resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree, string sourceAlias)
22+
{
23+
Process(resultOperator, queryModelVisitor, tree, null, sourceAlias);
24+
}
25+
26+
private void Process(
27+
FetchRequestBase resultOperator,
28+
QueryModelVisitor queryModelVisitor,
29+
IntermediateHqlTree tree,
30+
HqlTreeNode currentNode,
31+
string sourceAlias)
2232
{
2333
var memberPath = tree.TreeBuilder.Dot(
2434
tree.TreeBuilder.Ident(sourceAlias),
2535
tree.TreeBuilder.Ident(resultOperator.RelationMember.Name));
2636

27-
Process(resultOperator, queryModelVisitor, tree, memberPath, null);
37+
Process(resultOperator, queryModelVisitor, tree, memberPath, currentNode, null);
2838
}
2939

30-
private void Process(FetchRequestBase resultOperator, QueryModelVisitor queryModelVisitor, IntermediateHqlTree tree, HqlDot memberPath, IType propType)
40+
private void Process(
41+
FetchRequestBase resultOperator,
42+
QueryModelVisitor queryModelVisitor,
43+
IntermediateHqlTree tree,
44+
HqlDot memberPath,
45+
HqlTreeNode currentNode,
46+
IType propType)
3147
{
3248
if (resultOperator is FetchOneRequest)
3349
{
@@ -40,8 +56,14 @@ private void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMod
4056

4157
if (propType != null && !propType.IsAssociationType)
4258
{
43-
tree.AddFromLastChildClause(tree.TreeBuilder.Fetch());
44-
tree.AddFromLastChildClause(memberPath);
59+
if (currentNode == null)
60+
{
61+
currentNode = tree.GetFromRangeClause()
62+
?? throw new InvalidOperationException($"Property {resultOperator.RelationMember.Name} cannot be fetched for this type of query.");
63+
}
64+
65+
currentNode.AddChild(tree.TreeBuilder.Fetch());
66+
currentNode.AddChild(memberPath);
4567

4668
ComponentType componentType = null;
4769
foreach (var innerFetch in resultOperator.InnerFetchRequests)
@@ -61,20 +83,21 @@ private void Process(FetchRequestBase resultOperator, QueryModelVisitor queryMod
6183
memberPath,
6284
tree.TreeBuilder.Ident(innerFetch.RelationMember.Name));
6385

64-
Process(innerFetch, queryModelVisitor, tree, memberPath, componentType.Subtypes[subTypeIndex]);
86+
Process(innerFetch, queryModelVisitor, tree, memberPath, currentNode, componentType.Subtypes[subTypeIndex]);
6587
}
6688

6789
return;
6890
}
6991
}
7092

7193
var alias = queryModelVisitor.Model.GetNewName("_");
72-
tree.AddFromClause(tree.TreeBuilder.LeftFetchJoin(memberPath, tree.TreeBuilder.Alias(alias)));
94+
currentNode = tree.TreeBuilder.LeftFetchJoin(memberPath, tree.TreeBuilder.Alias(alias));
95+
tree.AddFromClause(currentNode);
7396
tree.AddDistinctRootOperator();
7497

7598
foreach (var innerFetch in resultOperator.InnerFetchRequests)
7699
{
77-
Process(innerFetch, queryModelVisitor, tree, alias);
100+
Process(innerFetch, queryModelVisitor, tree, currentNode, alias);
78101
}
79102
}
80103
}

0 commit comments

Comments
 (0)