diff --git a/src/NHibernate.Test/NHibernate.Test.csproj b/src/NHibernate.Test/NHibernate.Test.csproj index 4ce9639e5e2..6754961c9c0 100644 --- a/src/NHibernate.Test/NHibernate.Test.csproj +++ b/src/NHibernate.Test/NHibernate.Test.csproj @@ -1089,6 +1089,7 @@ + @@ -1096,6 +1097,7 @@ + diff --git a/src/NHibernate.Test/Stateless/FetchingLazyCollections/LazyCollectionFetchTests.cs b/src/NHibernate.Test/Stateless/FetchingLazyCollections/LazyCollectionFetchTests.cs index eab851b6ff1..418912d8d21 100644 --- a/src/NHibernate.Test/Stateless/FetchingLazyCollections/LazyCollectionFetchTests.cs +++ b/src/NHibernate.Test/Stateless/FetchingLazyCollections/LazyCollectionFetchTests.cs @@ -1,9 +1,11 @@ using System; using System.Collections.Generic; +using System.Linq; using Iesi.Collections.Generic; using NHibernate.Cfg.MappingSchema; using NHibernate.Mapping.ByCode; using NHibernate.Mapping.ByCode.Conformist; +using NHibernate.Linq; using NUnit.Framework; using SharpTestsEx; @@ -130,6 +132,24 @@ public void ShouldWorkLoadingComplexEntities() tx.Commit(); } + using (IStatelessSession s = sessions.OpenStatelessSession()) + using (ITransaction tx = s.BeginTransaction()) + { + IList> hf = s.Query>().FetchMany(f => f.Childs).ToList(); + Assert.That(hf.Count, Is.EqualTo(1)); + Assert.That(hf[0].Father.Name, Is.EqualTo(humanFather)); + Assert.That(hf[0].Mother.Name, Is.EqualTo(humanMother)); + NHibernateUtil.IsInitialized(hf[0].Childs).Should("Lazy collection should be initialized").Be.True(); + + IList> rf = s.Query>().FetchMany(f => f.Childs).ToList(); + Assert.That(rf.Count, Is.EqualTo(1)); + Assert.That(rf[0].Father.Description, Is.EqualTo(crocodileFather)); + Assert.That(rf[0].Mother.Description, Is.EqualTo(crocodileMother)); + NHibernateUtil.IsInitialized(hf[0].Childs).Should("Lazy collection should be initialized").Be.True(); + + tx.Commit(); + } + using (ISession s = sessions.OpenSession()) using (ITransaction tx = s.BeginTransaction()) { diff --git a/src/NHibernate.Test/Stateless/FetchingLazyCollections/TreeFetchTests.cs b/src/NHibernate.Test/Stateless/FetchingLazyCollections/TreeFetchTests.cs new file mode 100644 index 00000000000..4ac6b1f9aea --- /dev/null +++ b/src/NHibernate.Test/Stateless/FetchingLazyCollections/TreeFetchTests.cs @@ -0,0 +1,71 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using Iesi.Collections.Generic; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NHibernate.Mapping.ByCode.Conformist; +using NHibernate.Linq; +using NUnit.Framework; +using SharpTestsEx; + +namespace NHibernate.Test.Stateless.FetchingLazyCollections +{ + public class TreeFetchTests : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.BeforeMapClass += (mi, t, cm) => cm.Id(im => im.Generator(Generators.HighLow)); + mapper.Class( + mc => + { + mc.Id(x => x.Id); + mc.Property(x => x.Content); + mc.Set(x => x.Children, cam => + { + cam.Key(km => km.Column("parentId")); + cam.Cascade(Mapping.ByCode.Cascade.All); + }, rel => rel.OneToMany()); + }); + var mappings = mapper.CompileMappingForAllExplicitlyAddedEntities(); + return mappings; + } + + [Test] + public void FetchMultipleHierarchies() + { + using (ISession s = sessions.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + var root = new TreeNode { Content = "Root" }; + var child1 = new TreeNode { Content = "Child1" }; + root.Children.Add(child1); + root.Children.Add(new TreeNode { Content = "Child2" }); + child1.Children.Add(new TreeNode { Content = "Child1Child1" }); + child1.Children.Add(new TreeNode { Content = "Child1Child2" }); + s.Save(root); + tx.Commit(); + } + + using (IStatelessSession s = sessions.OpenStatelessSession()) + using (ITransaction tx = s.BeginTransaction()) + { + IList rootNodes = s.Query().Where(t => t.Content == "Root") + .FetchMany(f => f.Children) + .ThenFetchMany(f => f.Children).ToList(); + Assert.That(rootNodes.Count, Is.EqualTo(1)); + Assert.That(rootNodes.First().Children.Count, Is.EqualTo(2)); + + tx.Commit(); + } + + using (ISession s = sessions.OpenSession()) + using (ITransaction tx = s.BeginTransaction()) + { + s.Delete("from TreeNode"); + tx.Commit(); + } + } + } +} \ No newline at end of file diff --git a/src/NHibernate.Test/Stateless/TreeNode.cs b/src/NHibernate.Test/Stateless/TreeNode.cs new file mode 100644 index 00000000000..4c285e9de19 --- /dev/null +++ b/src/NHibernate.Test/Stateless/TreeNode.cs @@ -0,0 +1,23 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Iesi.Collections.Generic; + +namespace NHibernate.Test.Stateless +{ + public class TreeNode + { + private ISet children = new HashedSet(); + + public virtual int Id { get; protected set; } + + public virtual string Content { get; set; } + + public virtual ISet Children + { + get { return children; } + protected set { children = value; } + } + } +} diff --git a/src/NHibernate/Impl/StatelessSessionImpl.cs b/src/NHibernate/Impl/StatelessSessionImpl.cs index e9534b9ba48..1d9c90b60e9 100644 --- a/src/NHibernate/Impl/StatelessSessionImpl.cs +++ b/src/NHibernate/Impl/StatelessSessionImpl.cs @@ -356,7 +356,13 @@ public override bool IsEventSource public override object GetEntityUsingInterceptor(EntityKey key) { CheckAndUpdateSessionStatus(); - return null; + // while a pending Query we should use existing temporary entities so a join fetch does not create multiple instances + // of the same parent item + object obj; + if (temporaryPersistenceContext.EntitiesByKey.TryGetValue(key, out obj)) + return obj; + else + return null; } public override IPersistenceContext PersistenceContext