Skip to content

Commit 47992e7

Browse files
deAtogDavid Ellingsworthbahusoid
authored
Fix caching of OneToOneType from second level cache. (#2576)
Co-authored-by: David Ellingsworth <David.Ellingsworth@davey.com> Co-authored-by: Roman Artiukhin <bahusdrive@gmail.com>
1 parent 7f60bc3 commit 47992e7

File tree

12 files changed

+892
-59
lines changed

12 files changed

+892
-59
lines changed
Lines changed: 224 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,224 @@
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.Collections.Generic;
13+
using System.Linq;
14+
using NHibernate.Stat;
15+
using NUnit.Framework;
16+
using NHCfg = NHibernate.Cfg;
17+
using NHibernate.Linq;
18+
19+
namespace NHibernate.Test.NHSpecificTest.GH2552
20+
{
21+
using System.Threading.Tasks;
22+
using System.Threading;
23+
[TestFixture]
24+
public class FixtureAsync : BugTestCase
25+
{
26+
protected override string CacheConcurrencyStrategy => null;
27+
28+
protected override void Configure(NHCfg.Configuration configuration)
29+
{
30+
configuration.SetProperty(NHCfg.Environment.UseSecondLevelCache, "true");
31+
configuration.SetProperty(NHCfg.Environment.GenerateStatistics, "true");
32+
}
33+
34+
protected override void OnTearDown()
35+
{
36+
using (var s = OpenSession())
37+
using (var tx = s.BeginTransaction())
38+
{
39+
s.CreateQuery("delete from DetailsByFK").ExecuteUpdate();
40+
s.CreateQuery("delete from PersonByFK").ExecuteUpdate();
41+
s.CreateQuery("delete from DetailsByRef").ExecuteUpdate();
42+
s.CreateQuery("delete from PersonByRef").ExecuteUpdate();
43+
44+
tx.Commit();
45+
}
46+
47+
Sfi.Evict(typeof(PersonByFK));
48+
Sfi.Evict(typeof(DetailsByFK));
49+
Sfi.Evict(typeof(PersonByRef));
50+
Sfi.Evict(typeof(DetailsByRef));
51+
}
52+
53+
private async Task OneToOneFetchTestAsync<TPerson, TDetails>(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new()
54+
{
55+
List<object> ids = await (this.CreatePersonAndDetailsAsync<TPerson, TDetails>(cancellationToken));
56+
57+
IStatistics statistics = Sfi.Statistics;
58+
59+
// Clear the second level cache and the statistics
60+
await (Sfi.EvictEntityAsync(typeof(TPerson).FullName, cancellationToken));
61+
await (Sfi.EvictEntityAsync(typeof(TDetails).FullName, cancellationToken));
62+
await (Sfi.EvictQueriesAsync(cancellationToken));
63+
64+
statistics.Clear();
65+
66+
// Fill the empty caches with data.
67+
await (this.FetchPeopleByIdAsync<TPerson>(ids, cancellationToken));
68+
69+
// Verify that no data was retrieved from the cache.
70+
Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count");
71+
72+
statistics.Clear();
73+
74+
await (this.FetchPeopleByIdAsync<TPerson>(ids, cancellationToken));
75+
76+
Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count");
77+
}
78+
79+
private async Task OneToOneUpdateTestAsync<TPerson, TDetails>(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new()
80+
{
81+
List<object> ids = await (this.CreatePersonAndDetailsAsync<TPerson, TDetails>(cancellationToken));
82+
83+
IStatistics statistics = Sfi.Statistics;
84+
85+
// Clear the second level cache and the statistics
86+
await (Sfi.EvictEntityAsync(typeof(TPerson).FullName, cancellationToken));
87+
await (Sfi.EvictEntityAsync(typeof(TDetails).FullName, cancellationToken));
88+
await (Sfi.EvictQueriesAsync(cancellationToken));
89+
90+
statistics.Clear();
91+
92+
// Fill the empty caches with data.
93+
await (this.FetchPeopleByIdAsync<TPerson>(ids, cancellationToken));
94+
95+
// Verify that no data was retrieved from the cache.
96+
Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count");
97+
statistics.Clear();
98+
99+
int personId = await (DeleteDetailsFromFirstPersonAsync<TPerson>(cancellationToken));
100+
101+
// Verify that the cache was updated
102+
Assert.AreEqual(1, statistics.SecondLevelCachePutCount, "Second level cache put count");
103+
statistics.Clear();
104+
105+
// Verify that the Person was updated in the cache
106+
using (ISession s = Sfi.OpenSession())
107+
using (ITransaction tx = s.BeginTransaction())
108+
{
109+
TPerson person = await (s.GetAsync<TPerson>(personId, cancellationToken));
110+
111+
Assert.IsNull(person.Details);
112+
}
113+
114+
Assert.AreEqual(0, statistics.SecondLevelCacheMissCount, "Second level cache miss count");
115+
statistics.Clear();
116+
117+
// Verify that the Details was removed from the cache and deleted.
118+
using (ISession s = Sfi.OpenSession())
119+
using (ITransaction tx = s.BeginTransaction())
120+
{
121+
TDetails details = await (s.GetAsync<TDetails>(personId, cancellationToken));
122+
123+
Assert.Null(details);
124+
}
125+
126+
Assert.AreEqual(0, statistics.SecondLevelCacheHitCount, "Second level cache hit count");
127+
}
128+
129+
private async Task<int> DeleteDetailsFromFirstPersonAsync<TPerson>(CancellationToken cancellationToken = default(CancellationToken)) where TPerson:Person
130+
{
131+
using (ISession s = Sfi.OpenSession())
132+
using (ITransaction tx = s.BeginTransaction())
133+
{
134+
// Get the first person with details.
135+
Person person = await (s.Query<TPerson>()
136+
.Where(p => p.Details != null)
137+
.Take(1)
138+
.SingleOrDefaultAsync(cancellationToken));
139+
140+
Assert.NotNull(person);
141+
Assert.NotNull(person.Details);
142+
143+
person.Details = null;
144+
145+
await (tx.CommitAsync(cancellationToken));
146+
147+
return person.Id;
148+
}
149+
}
150+
151+
private async Task<List<object>> CreatePersonAndDetailsAsync<TPerson, TDetails>(CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person, new() where TDetails : Details, new()
152+
{
153+
List<object> ids = new List<object>();
154+
155+
using (ISession s = Sfi.OpenSession())
156+
using (ITransaction tx = s.BeginTransaction())
157+
{
158+
for (int i = 0; i < 6; i++)
159+
{
160+
Person person = new TPerson();
161+
162+
if (i % 2 == 0)
163+
{
164+
Details details = new TDetails();
165+
166+
details.Data = String.Format("{0}{1}", typeof(TDetails).Name, i);
167+
168+
person.Details = details;
169+
}
170+
171+
person.Name = String.Format("{0}{1}", typeof(TPerson).Name, i);
172+
173+
ids.Add(await (s.SaveAsync(person, cancellationToken)));
174+
}
175+
176+
await (tx.CommitAsync(cancellationToken));
177+
}
178+
179+
return ids;
180+
}
181+
182+
public async Task<IList<TPerson>> FetchPeopleByIdAsync<TPerson>(List<object> ids, CancellationToken cancellationToken = default(CancellationToken)) where TPerson : Person
183+
{
184+
IList<TPerson> people = new List<TPerson>();
185+
186+
using (ISession s = Sfi.OpenSession())
187+
using (ITransaction tx = s.BeginTransaction())
188+
{
189+
foreach (object id in ids)
190+
{
191+
people.Add(await (s.GetAsync<TPerson>(id, cancellationToken)));
192+
}
193+
194+
await (tx.CommitAsync(cancellationToken));
195+
}
196+
197+
return people;
198+
}
199+
200+
[Test]
201+
public async Task OneToOneCacheFetchByForeignKeyAsync()
202+
{
203+
await (OneToOneFetchTestAsync<PersonByFK, DetailsByFK>());
204+
}
205+
206+
[Test]
207+
public async Task OneToOneCacheFetchByRefAsync()
208+
{
209+
await (OneToOneFetchTestAsync<PersonByRef, DetailsByRef>());
210+
}
211+
212+
[Test]
213+
public async Task OneToOneCacheUpdateByForeignKeyAsync()
214+
{
215+
await (OneToOneUpdateTestAsync<PersonByFK, DetailsByFK>());
216+
}
217+
218+
[Test]
219+
public async Task OneToOneCacheUpdateByRefAsync()
220+
{
221+
await (OneToOneUpdateTestAsync<PersonByRef, DetailsByRef>());
222+
}
223+
}
224+
}
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
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.Test.NHSpecificTest;
12+
using NUnit.Framework;
13+
14+
namespace NHibernate.Test.OneToOneType
15+
{
16+
using System.Threading.Tasks;
17+
[TestFixture]
18+
public class FixtureAsync : BugTestCase
19+
{
20+
protected override void OnTearDown()
21+
{
22+
using (var s = Sfi.OpenSession())
23+
using (var tx = s.BeginTransaction())
24+
{
25+
s.CreateQuery("delete from Details").ExecuteUpdate();
26+
s.CreateQuery("delete from Owner").ExecuteUpdate();
27+
28+
tx.Commit();
29+
}
30+
}
31+
32+
[Test]
33+
public async Task OneToOnePersistedOnOwnerUpdateAsync()
34+
{
35+
object ownerId;
36+
37+
using (var s = Sfi.OpenSession())
38+
using (var tx = s.BeginTransaction())
39+
{
40+
var owner = new Owner()
41+
{
42+
Name = "Owner",
43+
};
44+
45+
ownerId = await (s.SaveAsync(owner));
46+
47+
await (tx.CommitAsync());
48+
}
49+
50+
using (var s = Sfi.OpenSession())
51+
using (var tx = s.BeginTransaction())
52+
{
53+
Owner owner = await (s.LoadAsync<Owner>(ownerId));
54+
55+
owner.Details = new Details()
56+
{
57+
Data = "Owner Details"
58+
};
59+
60+
await (tx.CommitAsync());
61+
}
62+
63+
using (var s = Sfi.OpenSession())
64+
using (var tx = s.BeginTransaction())
65+
{
66+
Owner owner = await (s.GetAsync<Owner>(ownerId));
67+
68+
Assert.NotNull(owner.Details);
69+
}
70+
}
71+
72+
[Test]
73+
public async Task OneToOnePersistedOnOwnerUpdateForSessionUpdateAsync()
74+
{
75+
Owner owner;
76+
77+
using (var s = Sfi.OpenSession())
78+
using (var tx = s.BeginTransaction())
79+
{
80+
owner = new Owner()
81+
{
82+
Name = "Owner",
83+
};
84+
85+
await (s.SaveAsync(owner));
86+
await (tx.CommitAsync());
87+
}
88+
89+
using (var s = Sfi.OpenSession())
90+
{
91+
owner = await (s.GetAsync<Owner>(owner.Id));
92+
}
93+
94+
using (var s = Sfi.OpenSession())
95+
using (var tx = s.BeginTransaction())
96+
{
97+
await (s.SaveOrUpdateAsync(owner));
98+
owner.Details = new Details()
99+
{
100+
Data = "Owner Details"
101+
};
102+
103+
await (tx.CommitAsync());
104+
}
105+
106+
using (var s = Sfi.OpenSession())
107+
{
108+
owner = await (s.GetAsync<Owner>(owner.Id));
109+
110+
Assert.IsNotNull(owner.Details);
111+
}
112+
}
113+
}
114+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
namespace NHibernate.Test.NHSpecificTest.GH2552
2+
{
3+
public abstract class Details
4+
{
5+
public virtual int Id { get; protected set; }
6+
public virtual Person Person { get; set; }
7+
public virtual string Data { get; set; }
8+
}
9+
public class DetailsByFK : Details { }
10+
public class DetailsByRef : Details { }
11+
}

0 commit comments

Comments
 (0)