Skip to content

Commit a29dbf8

Browse files
committed
Merge fix for NH-3015 Join fetch with Stateless Session returns duplicate entities
2 parents 6823bc4 + bbd00ba commit a29dbf8

File tree

5 files changed

+115
-0
lines changed

5 files changed

+115
-0
lines changed

src/NHibernate.Test/NHibernate.Test.csproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1233,13 +1233,15 @@
12331233
<Compile Include="RecordingInterceptor.cs" />
12341234
<Compile Include="Stateless\Contact.cs" />
12351235
<Compile Include="Stateless\Country.cs" />
1236+
<Compile Include="Stateless\FetchingLazyCollections\TreeFetchTests.cs" />
12361237
<Compile Include="Stateless\FetchingLazyCollections\LazyCollectionFetchTests.cs" />
12371238
<Compile Include="Stateless\Fetching\Resource.cs" />
12381239
<Compile Include="Stateless\Fetching\StatelessSessionFetchingTest.cs" />
12391240
<Compile Include="Stateless\Fetching\Task.cs" />
12401241
<Compile Include="Stateless\Fetching\User.cs" />
12411242
<Compile Include="Stateless\Org.cs" />
12421243
<Compile Include="Stateless\StatelessSessionQueryFixture.cs" />
1244+
<Compile Include="Stateless\TreeNode.cs" />
12431245
<Compile Include="Subselect\ClassSubselectFixture.cs" />
12441246
<Compile Include="Subselect\Domain.cs" />
12451247
<Compile Include="TestCaseMappingByCode.cs" />

src/NHibernate.Test/Stateless/FetchingLazyCollections/LazyCollectionFetchTests.cs

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.Linq;
34
using NHibernate.Cfg.MappingSchema;
45
using NHibernate.Mapping.ByCode;
56
using NHibernate.Mapping.ByCode.Conformist;
7+
using NHibernate.Linq;
68
using NUnit.Framework;
79
using SharpTestsEx;
810

@@ -129,6 +131,24 @@ public void ShouldWorkLoadingComplexEntities()
129131
tx.Commit();
130132
}
131133

134+
using (IStatelessSession s = sessions.OpenStatelessSession())
135+
using (ITransaction tx = s.BeginTransaction())
136+
{
137+
IList<Family<Human>> hf = s.Query<Family<Human>>().FetchMany(f => f.Childs).ToList();
138+
Assert.That(hf.Count, Is.EqualTo(1));
139+
Assert.That(hf[0].Father.Name, Is.EqualTo(humanFather));
140+
Assert.That(hf[0].Mother.Name, Is.EqualTo(humanMother));
141+
NHibernateUtil.IsInitialized(hf[0].Childs).Should("Lazy collection should be initialized").Be.True();
142+
143+
IList<Family<Reptile>> rf = s.Query<Family<Reptile>>().FetchMany(f => f.Childs).ToList();
144+
Assert.That(rf.Count, Is.EqualTo(1));
145+
Assert.That(rf[0].Father.Description, Is.EqualTo(crocodileFather));
146+
Assert.That(rf[0].Mother.Description, Is.EqualTo(crocodileMother));
147+
NHibernateUtil.IsInitialized(hf[0].Childs).Should("Lazy collection should be initialized").Be.True();
148+
149+
tx.Commit();
150+
}
151+
132152
using (ISession s = sessions.OpenSession())
133153
using (ITransaction tx = s.BeginTransaction())
134154
{
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using NHibernate.Cfg.MappingSchema;
4+
using NHibernate.Mapping.ByCode;
5+
using NHibernate.Linq;
6+
using NUnit.Framework;
7+
8+
namespace NHibernate.Test.Stateless.FetchingLazyCollections
9+
{
10+
public class TreeFetchTests : TestCaseMappingByCode
11+
{
12+
protected override HbmMapping GetMappings()
13+
{
14+
var mapper = new ModelMapper();
15+
mapper.BeforeMapClass += (mi, t, cm) => cm.Id(im => im.Generator(Generators.HighLow));
16+
mapper.Class<TreeNode>(
17+
mc =>
18+
{
19+
mc.Id(x => x.Id);
20+
mc.Property(x => x.Content);
21+
mc.Set(x => x.Children, cam =>
22+
{
23+
cam.Key(km => km.Column("parentId"));
24+
cam.Cascade(Mapping.ByCode.Cascade.All);
25+
}, rel => rel.OneToMany());
26+
});
27+
var mappings = mapper.CompileMappingForAllExplicitlyAddedEntities();
28+
return mappings;
29+
}
30+
31+
[Test]
32+
public void FetchMultipleHierarchies()
33+
{
34+
using (ISession s = sessions.OpenSession())
35+
using (ITransaction tx = s.BeginTransaction())
36+
{
37+
var root = new TreeNode {Content = "Root"};
38+
var child1 = new TreeNode {Content = "Child1"};
39+
root.Children.Add(child1);
40+
root.Children.Add(new TreeNode {Content = "Child2"});
41+
child1.Children.Add(new TreeNode {Content = "Child1Child1"});
42+
child1.Children.Add(new TreeNode {Content = "Child1Child2"});
43+
s.Save(root);
44+
tx.Commit();
45+
}
46+
47+
using (IStatelessSession s = sessions.OpenStatelessSession())
48+
using (ITransaction tx = s.BeginTransaction())
49+
{
50+
IList<TreeNode> rootNodes = s.Query<TreeNode>().Where(t => t.Content == "Root")
51+
.FetchMany(f => f.Children)
52+
.ThenFetchMany(f => f.Children).ToList();
53+
Assert.That(rootNodes.Count, Is.EqualTo(1));
54+
Assert.That(rootNodes.First().Children.Count, Is.EqualTo(2));
55+
56+
tx.Commit();
57+
}
58+
59+
using (ISession s = sessions.OpenSession())
60+
using (ITransaction tx = s.BeginTransaction())
61+
{
62+
s.Delete("from TreeNode");
63+
tx.Commit();
64+
}
65+
}
66+
}
67+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using System.Collections.Generic;
2+
3+
4+
namespace NHibernate.Test.Stateless
5+
{
6+
public class TreeNode
7+
{
8+
private ISet<TreeNode> _children = new HashSet<TreeNode>();
9+
10+
public virtual int Id { get; protected set; }
11+
12+
public virtual string Content { get; set; }
13+
14+
public virtual ISet<TreeNode> Children
15+
{
16+
get { return _children; }
17+
protected set { _children = value; }
18+
}
19+
}
20+
}

src/NHibernate/Impl/StatelessSessionImpl.cs

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -316,6 +316,12 @@ public override bool IsEventSource
316316
public override object GetEntityUsingInterceptor(EntityKey key)
317317
{
318318
CheckAndUpdateSessionStatus();
319+
// while a pending Query we should use existing temporary entities so a join fetch does not create multiple instances
320+
// of the same parent item (NH-3015, NH-3705).
321+
object obj;
322+
if (temporaryPersistenceContext.EntitiesByKey.TryGetValue(key, out obj))
323+
return obj;
324+
319325
return null;
320326
}
321327

0 commit comments

Comments
 (0)