diff --git a/src/NHibernate.Test/Async/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs b/src/NHibernate.Test/Async/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs new file mode 100644 index 00000000000..0a5b10467f9 --- /dev/null +++ b/src/NHibernate.Test/Async/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by AsyncGenerator. +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + + +using System.Collections.Generic; +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NHibernate.Transform; +using NHibernate.Util; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3134 +{ + using System.Threading.Tasks; + [TestFixture] + public class ManyToManyChildFetchByCodeFixtureAsync : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.AddMapping(); + mapper.AddMapping(); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var a1 = new A(); + a1.Bs.Add(new B()); + a1.Bs.Add(new B()); + session.Save(a1); + + var a2 = new A(); + a2.Bs.Add(new B()); + a2.Bs.Add(new B()); + session.Save(a2); + + transaction.Commit(); + } + } + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + transaction.Commit(); + } + } + + [Test] + public async Task ChildFetchQueryOverAsync() + { + using (var session = OpenSession()) + { + session.QueryOver().Future(); + + session.QueryOver() + .Fetch(SelectMode.ChildFetch, b => b) + .Fetch(SelectMode.Fetch, b => b.As) + .Future(); + + var result = (await (session.QueryOver() + .Fetch(SelectMode.ChildFetch, b => b, b => b.As) + .Fetch(SelectMode.Fetch, b => b.As.First().Bs) + .TransformUsing(Transformers.DistinctRootEntity) + .Future() + .GetEnumerableAsync())) + .ToList(); + + Assert.That(result.Count, Is.EqualTo(4)); + Assert.That(NHibernateUtil.IsInitialized(result[0].As), Is.True); + Assert.That(NHibernateUtil.IsInitialized(result[0].As.First().Bs), Is.True); + Assert.That(result[0].As.Count, Is.EqualTo(1)); + Assert.That(result[0].As.First().Bs.Count, Is.EqualTo(2)); + } + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/A.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/A.cs new file mode 100644 index 00000000000..fab1a02c8ff --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3134/A.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.GH3134 +{ + public class A + { + public virtual Guid Id { get; set; } + public virtual string NameA { get; set; } + public virtual ISet Bs { get; set; } = new HashSet(); + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/AMap.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/AMap.cs new file mode 100644 index 00000000000..5e02c36a746 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3134/AMap.cs @@ -0,0 +1,24 @@ +using NHibernate.Mapping.ByCode; +using NHibernate.Mapping.ByCode.Conformist; + +namespace NHibernate.Test.NHSpecificTest.GH3134 +{ + public class AMap : ClassMapping + { + public AMap() + { + this.Table("A"); + this.Id(x => x.Id, mapper => { mapper.Column("IDA"); mapper.Generator(Generators.Guid); }); + Property(x => x.NameA); + this.Set( + x => x.Bs, + collectionMapping => + { + collectionMapping.Table("AB"); + collectionMapping.Key(k => { k.Column("A"); k.ForeignKey("none");}); + collectionMapping.Cascade(Mapping.ByCode.Cascade.All); + }, + map => map.ManyToMany(m => { m.Column("B"); })); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/B.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/B.cs new file mode 100644 index 00000000000..c06b30226e8 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3134/B.cs @@ -0,0 +1,12 @@ +using System; +using System.Collections.Generic; + +namespace NHibernate.Test.NHSpecificTest.GH3134 +{ + public class B + { + public virtual Guid Id { get; set; } + public virtual string NameB { get; set; } + public virtual ISet As { get; set; } = new HashSet(); + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/BMap.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/BMap.cs new file mode 100644 index 00000000000..998d4ffbbb5 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3134/BMap.cs @@ -0,0 +1,24 @@ +using NHibernate.Mapping.ByCode; +using NHibernate.Mapping.ByCode.Conformist; + +namespace NHibernate.Test.NHSpecificTest.GH3134 +{ + public class BMap : ClassMapping + { + public BMap() + { + this.Table("B"); + this.Id(x => x.Id, mapper => { mapper.Column("IDB"); mapper.Generator(Generators.Guid); }); + Property(x => x.NameB); + this.Set( + x => x.As, + collectionMapping => + { + collectionMapping.Table("AB"); + collectionMapping.Key(k => { k.Column("B"); k.ForeignKey("none"); }); + collectionMapping.Cascade(Mapping.ByCode.Cascade.All); + }, + map => map.ManyToMany(m => { m.Column("A"); })); + } + } +} diff --git a/src/NHibernate.Test/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs b/src/NHibernate.Test/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs new file mode 100644 index 00000000000..78f359da012 --- /dev/null +++ b/src/NHibernate.Test/NHSpecificTest/GH3134/ManyToManyChildFetchByCodeFixture.cs @@ -0,0 +1,79 @@ +using System.Collections.Generic; +using System.Linq; +using NHibernate.Cfg.MappingSchema; +using NHibernate.Mapping.ByCode; +using NHibernate.Transform; +using NHibernate.Util; +using NUnit.Framework; + +namespace NHibernate.Test.NHSpecificTest.GH3134 +{ + [TestFixture] + public class ManyToManyChildFetchByCodeFixture : TestCaseMappingByCode + { + protected override HbmMapping GetMappings() + { + var mapper = new ModelMapper(); + mapper.AddMapping(); + mapper.AddMapping(); + + return mapper.CompileMappingForAllExplicitlyAddedEntities(); + } + protected override void OnSetUp() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + var a1 = new A(); + a1.Bs.Add(new B()); + a1.Bs.Add(new B()); + session.Save(a1); + + var a2 = new A(); + a2.Bs.Add(new B()); + a2.Bs.Add(new B()); + session.Save(a2); + + transaction.Commit(); + } + } + protected override void OnTearDown() + { + using (var session = OpenSession()) + using (var transaction = session.BeginTransaction()) + { + session.Delete("from System.Object"); + + transaction.Commit(); + } + } + + [Test] + public void ChildFetchQueryOver() + { + using (var session = OpenSession()) + { + session.QueryOver().Future(); + + session.QueryOver() + .Fetch(SelectMode.ChildFetch, b => b) + .Fetch(SelectMode.Fetch, b => b.As) + .Future(); + + var result = session.QueryOver() + .Fetch(SelectMode.ChildFetch, b => b, b => b.As) + .Fetch(SelectMode.Fetch, b => b.As.First().Bs) + .TransformUsing(Transformers.DistinctRootEntity) + .Future() + .GetEnumerable() + .ToList(); + + Assert.That(result.Count, Is.EqualTo(4)); + Assert.That(NHibernateUtil.IsInitialized(result[0].As), Is.True); + Assert.That(NHibernateUtil.IsInitialized(result[0].As.First().Bs), Is.True); + Assert.That(result[0].As.Count, Is.EqualTo(1)); + Assert.That(result[0].As.First().Bs.Count, Is.EqualTo(2)); + } + } + } +} diff --git a/src/NHibernate/Loader/OuterJoinableAssociation.cs b/src/NHibernate/Loader/OuterJoinableAssociation.cs index 4f50cbfaf3c..c15615bcfe6 100644 --- a/src/NHibernate/Loader/OuterJoinableAssociation.cs +++ b/src/NHibernate/Loader/OuterJoinableAssociation.cs @@ -242,6 +242,9 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix, IncludeLazyProps = SelectMode == SelectMode.FetchLazyProperties, }); case SelectMode.ChildFetch: + // Skip ChildFetch for many-to-many as element id is added by element persister. + if (Joinable.IsCollection && ((IQueryableCollection) Joinable).IsManyToMany) + return string.Empty; return ReflectHelper.CastOrThrow(Joinable, "child fetch select mode") .IdentifierSelectFragment(RHSAlias, entitySuffix);