Skip to content

Commit 2f7250b

Browse files
deAtogbahusoidhazzik
authored
Fix loop detection for associations with fetch="join" mapping (#2718)
Add 'detect_fetch_loops' option to disable loop detection Fixes #2201 Co-authored-by: Roman Artiukhin <bahusdrive@gmail.com> Co-authored-by: Alex Zaytsev <hazzik@gmail.com>
1 parent 6c70217 commit 2f7250b

23 files changed

+1011
-46
lines changed

src/NHibernate.Test/Async/Immutable/EntityWithMutableCollection/AbstractEntityWithOneToManyTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1234,7 +1234,6 @@ public virtual async Task OneToManyCollectionOptimisticLockingWithUpdateAsync()
12341234

12351235
s = OpenSession();
12361236
t = s.BeginTransaction();
1237-
c = await (s.CreateCriteria<Contract>().UniqueResultAsync<Contract>());
12381237
// If the entity uses a join mapping, DML queries require temp tables.
12391238
if (Dialect.SupportsTemporaryTables)
12401239
await (s.CreateQuery("delete from Party").ExecuteUpdateAsync());
@@ -1251,7 +1250,7 @@ public virtual async Task OneToManyCollectionOptimisticLockingWithUpdateAsync()
12511250
await (s.DeleteAsync(partyOrig));
12521251
await (s.DeleteAsync(newParty));
12531252
}
1254-
1253+
c = await (s.CreateCriteria<Contract>().UniqueResultAsync<Contract>());
12551254
await (s.DeleteAsync(c));
12561255
Assert.That(await (s.CreateCriteria<Contract>().SetProjection(Projections.RowCountInt64()).UniqueResultAsync<long>()), Is.EqualTo(0L));
12571256
Assert.That(await (s.CreateCriteria<Party>().SetProjection(Projections.RowCountInt64()).UniqueResultAsync<long>()), Is.EqualTo(0L));
Lines changed: 66 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
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 NHibernate.Cfg;
12+
using NUnit.Framework;
13+
14+
namespace NHibernate.Test.NHSpecificTest.GH2201
15+
{
16+
using System.Threading.Tasks;
17+
[TestFixture]
18+
public class CircularReferenceFetchDepth0FixtureAsync : BaseFetchFixture
19+
{
20+
private int _id2;
21+
22+
public CircularReferenceFetchDepth0FixtureAsync() : base(0)
23+
{
24+
}
25+
26+
protected override void Configure(Configuration configuration)
27+
{
28+
configuration.SetProperty("max_fetch_depth", "0");
29+
base.Configure(configuration);
30+
}
31+
32+
protected override void OnSetUp()
33+
{
34+
base.OnSetUp();
35+
_id2 = _id;
36+
//Generate another test entity
37+
base.OnSetUp();
38+
}
39+
40+
[Test]
41+
public async Task QueryOverAsync()
42+
{
43+
using (var session = OpenSession())
44+
{
45+
Entity e1 = null;
46+
Entity e2 = null;
47+
var result = await (session.QueryOver<Entity>(() => e1)
48+
.JoinEntityAlias(() => e2, () => e2.EntityNumber == e1.EntityNumber && e2.EntityId != _id)
49+
.Where(e => e.EntityId == _id).SingleOrDefaultAsync());
50+
51+
VerifyChildrenNotInitialized(result);
52+
VerifyChildrenNotInitialized(await (session.LoadAsync<Entity>(_id2)));
53+
}
54+
}
55+
56+
[Test]
57+
public async Task GetAsync()
58+
{
59+
using (var session = OpenSession())
60+
{
61+
var result = await (session.GetAsync<Entity>(_id));
62+
VerifyChildrenNotInitialized(result);
63+
}
64+
}
65+
}
66+
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
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 NHibernate.Cfg;
12+
using NUnit.Framework;
13+
14+
namespace NHibernate.Test.NHSpecificTest.GH2201
15+
{
16+
using System.Threading.Tasks;
17+
[TestFixture(1)]
18+
[TestFixture(2)]
19+
public class CircularReferenceFetchDepthFixtureAsync : BaseFetchFixture
20+
{
21+
private int _id2;
22+
private int _id3;
23+
24+
public CircularReferenceFetchDepthFixtureAsync(int depth) : base(depth)
25+
{
26+
}
27+
28+
protected override void Configure(Configuration configuration)
29+
{
30+
configuration.SetProperty("max_fetch_depth", _depth.ToString());
31+
base.Configure(configuration);
32+
}
33+
34+
protected override void OnSetUp()
35+
{
36+
base.OnSetUp();
37+
_id2 = _id;
38+
39+
//Generate another test entities
40+
base.OnSetUp();
41+
_id3 = _id;
42+
base.OnSetUp();
43+
}
44+
45+
[Test]
46+
public async Task QueryOverAsync()
47+
{
48+
using (var session = OpenSession())
49+
{
50+
Entity e1 = null;
51+
Entity e2 = null;
52+
Entity e3 = null;
53+
var result = await (session.QueryOver<Entity>(() => e1)
54+
.JoinEntityAlias(() => e2, () => e2.EntityNumber == e1.EntityNumber && e2.EntityId == _id2)
55+
.JoinEntityAlias(() => e3, () => e3.EntityNumber == e1.EntityNumber && e3.EntityId == _id3)
56+
.Where(e => e.EntityId == _id).SingleOrDefaultAsync());
57+
58+
Verify(result);
59+
60+
Verify(await (session.LoadAsync<Entity>(_id2)));
61+
Verify(await (session.LoadAsync<Entity>(_id3)));
62+
}
63+
}
64+
65+
[Test]
66+
public async Task GetAsync()
67+
{
68+
using (var session = OpenSession())
69+
{
70+
var result = await (session.GetAsync<Entity>(_id));
71+
Verify(result);
72+
}
73+
}
74+
}
75+
}
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
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;
12+
using System.Linq;
13+
using NUnit.Framework;
14+
15+
namespace NHibernate.Test.NHSpecificTest.GH2201
16+
{
17+
using System.Threading.Tasks;
18+
[TestFixture]
19+
public class CircularReferenceFetchFixtureAsync : BaseFetchFixture
20+
{
21+
public CircularReferenceFetchFixtureAsync() : base(-1)
22+
{
23+
}
24+
25+
[Test]
26+
public async Task QueryOverAsync()
27+
{
28+
using (var session = OpenSession())
29+
{
30+
var result = await (session.QueryOver<Entity>().Where(e => e.EntityNumber == "Bob").SingleOrDefaultAsync());
31+
32+
Verify(result);
33+
}
34+
}
35+
36+
[Test]
37+
public async Task GetAsync()
38+
{
39+
using (var session = OpenSession())
40+
{
41+
var result = await (session.GetAsync<Entity>(_id));
42+
43+
Verify(result);
44+
}
45+
}
46+
}
47+
}
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
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 NUnit.Framework;
12+
using NHCfg = NHibernate.Cfg;
13+
14+
namespace NHibernate.Test.NHSpecificTest.GH2201
15+
{
16+
using System.Threading.Tasks;
17+
[TestFixture]
18+
public class DetectFetchLoopsFalseFixtureAsync : BugTestCase
19+
{
20+
protected override void Configure(NHCfg.Configuration configuration)
21+
{
22+
configuration.SetProperty(NHCfg.Environment.GenerateStatistics, "true");
23+
configuration.SetProperty(NHCfg.Environment.DetectFetchLoops, "false");
24+
}
25+
26+
protected override void OnTearDown()
27+
{
28+
using (var s = OpenSession())
29+
using (var tx = s.BeginTransaction())
30+
{
31+
s.Delete("from Person");
32+
33+
tx.Commit();
34+
}
35+
}
36+
37+
protected override void OnSetUp()
38+
{
39+
using (var s = OpenSession())
40+
using (var tx = s.BeginTransaction())
41+
{
42+
string[] names = { "Alice", "Bob" };
43+
44+
for (int i = 0; i < names.Length; i++)
45+
{
46+
var name = names[i];
47+
48+
var parent = new Person()
49+
{
50+
Name = name,
51+
Details = new Detail()
52+
{
53+
Data = $"Details for ${name}"
54+
}
55+
};
56+
57+
for (int j = 1; j <= 3; j++)
58+
{
59+
var child = new Person()
60+
{
61+
Name = $"Child ${j} of ${parent.Name}",
62+
Parent = parent,
63+
Details = new Detail()
64+
{
65+
Data = $"Details for child ${j} of ${name}"
66+
}
67+
};
68+
69+
parent.Children.Add(child);
70+
}
71+
72+
s.Save(parent);
73+
}
74+
75+
tx.Commit();
76+
}
77+
}
78+
79+
[Test]
80+
public async Task QueryOverPersonWithParentAsync()
81+
{
82+
var stats = Sfi.Statistics;
83+
84+
stats.Clear();
85+
86+
using (var s = OpenSession())
87+
using (var tx = s.BeginTransaction())
88+
{
89+
var people = await (s.QueryOver<Person>()
90+
.Fetch(SelectMode.Fetch, p => p.Parent)
91+
.Where(p => p.Parent != null)
92+
.ListAsync());
93+
94+
foreach (Person p in people)
95+
{
96+
Assert.That(p.Parent, Is.Not.Null);
97+
Assert.That(p.Parent.Details, Is.Not.Null);
98+
}
99+
100+
Assert.That(people.Count, Is.EqualTo(6));
101+
Assert.That(stats.QueryExecutionCount, Is.EqualTo(1));
102+
Assert.That(stats.EntityFetchCount, Is.EqualTo(0));
103+
Assert.That(stats.EntityLoadCount, Is.EqualTo(16));
104+
}
105+
}
106+
107+
[Test]
108+
public async Task QueryOverSinglePersonWithParentAsync()
109+
{
110+
var stats = Sfi.Statistics;
111+
112+
stats.Clear();
113+
114+
using (var s = OpenSession())
115+
using (var tx = s.BeginTransaction())
116+
{
117+
var person = await (s.QueryOver<Person>()
118+
.Where(p => p.Parent != null)
119+
.Fetch(SelectMode.Fetch, p => p.Parent)
120+
.Take(1)
121+
.SingleOrDefaultAsync());
122+
123+
Assert.That(person, Is.Not.Null);
124+
Assert.That(stats.QueryExecutionCount, Is.EqualTo(1));
125+
Assert.That(stats.EntityFetchCount, Is.EqualTo(0));
126+
Assert.That(stats.EntityLoadCount, Is.EqualTo(4));
127+
}
128+
}
129+
}
130+
}

src/NHibernate.Test/Immutable/EntityWithMutableCollection/AbstractEntityWithOneToManyTest.cs

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1223,7 +1223,6 @@ public virtual void OneToManyCollectionOptimisticLockingWithUpdate()
12231223

12241224
s = OpenSession();
12251225
t = s.BeginTransaction();
1226-
c = s.CreateCriteria<Contract>().UniqueResult<Contract>();
12271226
// If the entity uses a join mapping, DML queries require temp tables.
12281227
if (Dialect.SupportsTemporaryTables)
12291228
s.CreateQuery("delete from Party").ExecuteUpdate();
@@ -1240,7 +1239,7 @@ public virtual void OneToManyCollectionOptimisticLockingWithUpdate()
12401239
s.Delete(partyOrig);
12411240
s.Delete(newParty);
12421241
}
1243-
1242+
c = s.CreateCriteria<Contract>().UniqueResult<Contract>();
12441243
s.Delete(c);
12451244
Assert.That(s.CreateCriteria<Contract>().SetProjection(Projections.RowCountInt64()).UniqueResult<long>(), Is.EqualTo(0L));
12461245
Assert.That(s.CreateCriteria<Party>().SetProjection(Projections.RowCountInt64()).UniqueResult<long>(), Is.EqualTo(0L));

0 commit comments

Comments
 (0)