Skip to content

Commit 4db3735

Browse files
authored
Fix SelectMode.ChildFetch for many-to-many (#3140)
Fixes #3134
1 parent 4727c0c commit 4db3735

File tree

7 files changed

+244
-0
lines changed

7 files changed

+244
-0
lines changed
Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//------------------------------------------------------------------------------
2+
// <auto-generated>
3+
// This code was generated by AsyncGenerator.
4+
//
5+
// Changes to this file may cause incorrect behavior and will be lost if
6+
// the code is regenerated.
7+
// </auto-generated>
8+
//------------------------------------------------------------------------------
9+
10+
11+
using System.Collections.Generic;
12+
using System.Linq;
13+
using NHibernate.Cfg.MappingSchema;
14+
using NHibernate.Mapping.ByCode;
15+
using NHibernate.Transform;
16+
using NHibernate.Util;
17+
using NUnit.Framework;
18+
19+
namespace NHibernate.Test.NHSpecificTest.GH3134
20+
{
21+
using System.Threading.Tasks;
22+
[TestFixture]
23+
public class ManyToManyChildFetchByCodeFixtureAsync : TestCaseMappingByCode
24+
{
25+
protected override HbmMapping GetMappings()
26+
{
27+
var mapper = new ModelMapper();
28+
mapper.AddMapping<AMap>();
29+
mapper.AddMapping<BMap>();
30+
31+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
32+
}
33+
protected override void OnSetUp()
34+
{
35+
using (var session = OpenSession())
36+
using (var transaction = session.BeginTransaction())
37+
{
38+
var a1 = new A();
39+
a1.Bs.Add(new B());
40+
a1.Bs.Add(new B());
41+
session.Save(a1);
42+
43+
var a2 = new A();
44+
a2.Bs.Add(new B());
45+
a2.Bs.Add(new B());
46+
session.Save(a2);
47+
48+
transaction.Commit();
49+
}
50+
}
51+
protected override void OnTearDown()
52+
{
53+
using (var session = OpenSession())
54+
using (var transaction = session.BeginTransaction())
55+
{
56+
session.Delete("from System.Object");
57+
58+
transaction.Commit();
59+
}
60+
}
61+
62+
[Test]
63+
public async Task ChildFetchQueryOverAsync()
64+
{
65+
using (var session = OpenSession())
66+
{
67+
session.QueryOver<B>().Future();
68+
69+
session.QueryOver<B>()
70+
.Fetch(SelectMode.ChildFetch, b => b)
71+
.Fetch(SelectMode.Fetch, b => b.As)
72+
.Future();
73+
74+
var result = (await (session.QueryOver<B>()
75+
.Fetch(SelectMode.ChildFetch, b => b, b => b.As)
76+
.Fetch(SelectMode.Fetch, b => b.As.First().Bs)
77+
.TransformUsing(Transformers.DistinctRootEntity)
78+
.Future()
79+
.GetEnumerableAsync()))
80+
.ToList();
81+
82+
Assert.That(result.Count, Is.EqualTo(4));
83+
Assert.That(NHibernateUtil.IsInitialized(result[0].As), Is.True);
84+
Assert.That(NHibernateUtil.IsInitialized(result[0].As.First().Bs), Is.True);
85+
Assert.That(result[0].As.Count, Is.EqualTo(1));
86+
Assert.That(result[0].As.First().Bs.Count, Is.EqualTo(2));
87+
}
88+
}
89+
}
90+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH3134
5+
{
6+
public class A
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string NameA { get; set; }
10+
public virtual ISet<B> Bs { get; set; } = new HashSet<B>();
11+
}
12+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using NHibernate.Mapping.ByCode;
2+
using NHibernate.Mapping.ByCode.Conformist;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH3134
5+
{
6+
public class AMap : ClassMapping<A>
7+
{
8+
public AMap()
9+
{
10+
this.Table("A");
11+
this.Id(x => x.Id, mapper => { mapper.Column("IDA"); mapper.Generator(Generators.Guid); });
12+
Property(x => x.NameA);
13+
this.Set(
14+
x => x.Bs,
15+
collectionMapping =>
16+
{
17+
collectionMapping.Table("AB");
18+
collectionMapping.Key(k => { k.Column("A"); k.ForeignKey("none");});
19+
collectionMapping.Cascade(Mapping.ByCode.Cascade.All);
20+
},
21+
map => map.ManyToMany(m => { m.Column("B"); }));
22+
}
23+
}
24+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
using System;
2+
using System.Collections.Generic;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH3134
5+
{
6+
public class B
7+
{
8+
public virtual Guid Id { get; set; }
9+
public virtual string NameB { get; set; }
10+
public virtual ISet<A> As { get; set; } = new HashSet<A>();
11+
}
12+
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
using NHibernate.Mapping.ByCode;
2+
using NHibernate.Mapping.ByCode.Conformist;
3+
4+
namespace NHibernate.Test.NHSpecificTest.GH3134
5+
{
6+
public class BMap : ClassMapping<B>
7+
{
8+
public BMap()
9+
{
10+
this.Table("B");
11+
this.Id(x => x.Id, mapper => { mapper.Column("IDB"); mapper.Generator(Generators.Guid); });
12+
Property(x => x.NameB);
13+
this.Set(
14+
x => x.As,
15+
collectionMapping =>
16+
{
17+
collectionMapping.Table("AB");
18+
collectionMapping.Key(k => { k.Column("B"); k.ForeignKey("none"); });
19+
collectionMapping.Cascade(Mapping.ByCode.Cascade.All);
20+
},
21+
map => map.ManyToMany(m => { m.Column("A"); }));
22+
}
23+
}
24+
}
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using NHibernate.Cfg.MappingSchema;
4+
using NHibernate.Mapping.ByCode;
5+
using NHibernate.Transform;
6+
using NHibernate.Util;
7+
using NUnit.Framework;
8+
9+
namespace NHibernate.Test.NHSpecificTest.GH3134
10+
{
11+
[TestFixture]
12+
public class ManyToManyChildFetchByCodeFixture : TestCaseMappingByCode
13+
{
14+
protected override HbmMapping GetMappings()
15+
{
16+
var mapper = new ModelMapper();
17+
mapper.AddMapping<AMap>();
18+
mapper.AddMapping<BMap>();
19+
20+
return mapper.CompileMappingForAllExplicitlyAddedEntities();
21+
}
22+
protected override void OnSetUp()
23+
{
24+
using (var session = OpenSession())
25+
using (var transaction = session.BeginTransaction())
26+
{
27+
var a1 = new A();
28+
a1.Bs.Add(new B());
29+
a1.Bs.Add(new B());
30+
session.Save(a1);
31+
32+
var a2 = new A();
33+
a2.Bs.Add(new B());
34+
a2.Bs.Add(new B());
35+
session.Save(a2);
36+
37+
transaction.Commit();
38+
}
39+
}
40+
protected override void OnTearDown()
41+
{
42+
using (var session = OpenSession())
43+
using (var transaction = session.BeginTransaction())
44+
{
45+
session.Delete("from System.Object");
46+
47+
transaction.Commit();
48+
}
49+
}
50+
51+
[Test]
52+
public void ChildFetchQueryOver()
53+
{
54+
using (var session = OpenSession())
55+
{
56+
session.QueryOver<B>().Future();
57+
58+
session.QueryOver<B>()
59+
.Fetch(SelectMode.ChildFetch, b => b)
60+
.Fetch(SelectMode.Fetch, b => b.As)
61+
.Future();
62+
63+
var result = session.QueryOver<B>()
64+
.Fetch(SelectMode.ChildFetch, b => b, b => b.As)
65+
.Fetch(SelectMode.Fetch, b => b.As.First().Bs)
66+
.TransformUsing(Transformers.DistinctRootEntity)
67+
.Future()
68+
.GetEnumerable()
69+
.ToList();
70+
71+
Assert.That(result.Count, Is.EqualTo(4));
72+
Assert.That(NHibernateUtil.IsInitialized(result[0].As), Is.True);
73+
Assert.That(NHibernateUtil.IsInitialized(result[0].As.First().Bs), Is.True);
74+
Assert.That(result[0].As.Count, Is.EqualTo(1));
75+
Assert.That(result[0].As.First().Bs.Count, Is.EqualTo(2));
76+
}
77+
}
78+
}
79+
}

src/NHibernate/Loader/OuterJoinableAssociation.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,9 @@ internal string GetSelectFragment(string entitySuffix, string collectionSuffix,
242242
IncludeLazyProps = SelectMode == SelectMode.FetchLazyProperties,
243243
});
244244
case SelectMode.ChildFetch:
245+
// Skip ChildFetch for many-to-many as element id is added by element persister.
246+
if (Joinable.IsCollection && ((IQueryableCollection) Joinable).IsManyToMany)
247+
return string.Empty;
245248
return ReflectHelper.CastOrThrow<ISupportSelectModeJoinable>(Joinable, "child fetch select mode")
246249
.IdentifierSelectFragment(RHSAlias, entitySuffix);
247250

0 commit comments

Comments
 (0)