Skip to content

Commit 00ac90f

Browse files
bahusoidfredericDelaporte
authored andcommitted
Integrate universal query batch in Future
Fixes #752 Fixes #1293 Fixes partially #840 (multi-criteria/query not fixed) Fixes partially #1344 (multi-criteria not fixed)
1 parent 8649f6e commit 00ac90f

29 files changed

+990
-29
lines changed

src/NHibernate.Test/Async/Futures/LinqFutureFixture.cs

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -176,6 +176,36 @@ public async Task CanUseFutureQueryAsync()
176176
}
177177
}
178178

179+
[Test]
180+
public async Task CanUseFutureQueryAndQueryOverForSatelessSessionAsync()
181+
{
182+
IgnoreThisTestIfMultipleQueriesArentSupportedByDriver();
183+
184+
using (var s = Sfi.OpenStatelessSession())
185+
{
186+
var persons10 = s.Query<Person>()
187+
.Take(10)
188+
.ToFuture();
189+
var persons5 = s.QueryOver<Person>()
190+
.Take(5)
191+
.Future();
192+
193+
using (var logSpy = new SqlLogSpy())
194+
{
195+
foreach (var person in await (persons5.GetEnumerableAsync()))
196+
{
197+
}
198+
199+
foreach (var person in await (persons10.GetEnumerableAsync()))
200+
{
201+
}
202+
203+
var events = logSpy.Appender.GetEvents();
204+
Assert.AreEqual(1, events.Length);
205+
}
206+
}
207+
}
208+
179209
[Test]
180210
public async Task CanUseFutureQueryWithAnonymousTypeAsync()
181211
{
@@ -412,5 +442,120 @@ public async Task UsingManyParametersAndQueries_DoesNotCauseParameterNameCollisi
412442
await (tx.CommitAsync());
413443
}
414444
}
445+
446+
[Test]
447+
public async Task FutureCombineCachedAndNonCachedQueriesAsync()
448+
{
449+
using (var s = OpenSession())
450+
using (var tx = s.BeginTransaction())
451+
{
452+
var p1 = new Person
453+
{
454+
Name = "Person name",
455+
Age = 15
456+
};
457+
var p2 = new Person
458+
{
459+
Name = "Person name",
460+
Age = 20
461+
};
462+
463+
await (s.SaveAsync(p1));
464+
await (s.SaveAsync(p2));
465+
await (tx.CommitAsync());
466+
}
467+
468+
using (var s = Sfi.OpenSession())
469+
{
470+
var list = new List<IFutureEnumerable<Person>>();
471+
for (var i = 0; i < 5; i++)
472+
{
473+
var i1 = i;
474+
var query = s.Query<Person>().Where(x => x.Age > i1);
475+
list.Add(query.WithOptions(x => x.SetCacheable(true)).ToFuture());
476+
}
477+
478+
foreach (var query in list)
479+
{
480+
var result = (await (query.GetEnumerableAsync())).ToList();
481+
Assert.That(result.Count, Is.EqualTo(2));
482+
}
483+
}
484+
485+
//Check query.List returns data from cache
486+
Sfi.Statistics.IsStatisticsEnabled = true;
487+
using (var s = Sfi.OpenSession())
488+
{
489+
var list = new List<IEnumerable<Person>>();
490+
for (var i = 0; i < 5; i++)
491+
{
492+
var i1 = i;
493+
var query = s.Query<Person>().Where(x => x.Age > i1);
494+
495+
list.Add(await (query.WithOptions(x => x.SetCacheable(true)).ToListAsync()));
496+
}
497+
498+
foreach (var query in list)
499+
{
500+
var result = query.ToList();
501+
Assert.That(result.Count, Is.EqualTo(2));
502+
}
503+
504+
Assert.That(Sfi.Statistics.PrepareStatementCount, Is.EqualTo(0), "Queries must be retrieved from cache");
505+
}
506+
507+
//Check another Future returns data from cache
508+
Sfi.Statistics.Clear();
509+
using (var s = Sfi.OpenSession())
510+
{
511+
var list = new List<IFutureEnumerable<Person>>();
512+
//Reverse order of queries added to cache
513+
for (var i = 5 - 1; i >= 0; i--)
514+
{
515+
var i1 = i;
516+
var query = s.Query<Person>().Where(x => x.Age > i1);
517+
518+
list.Add(query.WithOptions(x => x.SetCacheable(true)).ToFuture());
519+
}
520+
521+
foreach (var query in list)
522+
{
523+
var result = (await (query.GetEnumerableAsync())).ToList();
524+
Assert.That(result.Count, Is.EqualTo(2));
525+
}
526+
527+
Assert.That(Sfi.Statistics.PrepareStatementCount , Is.EqualTo(0), "Future queries must be retrieved from cache");
528+
}
529+
530+
using (var s = OpenSession())
531+
using (var tx = s.BeginTransaction())
532+
{
533+
await (s.DeleteAsync("from Person"));
534+
await (tx.CommitAsync());
535+
}
536+
}
537+
538+
[Test]
539+
public async Task FutureAutoFlushAsync()
540+
{
541+
using (var s = OpenSession())
542+
using (var tx = s.BeginTransaction())
543+
{
544+
s.FlushMode = FlushMode.Auto;
545+
var p1 = new Person
546+
{
547+
Name = "Person name",
548+
Age = 15
549+
};
550+
await (s.SaveAsync(p1));
551+
await (s.FlushAsync());
552+
553+
await (s.DeleteAsync(p1));
554+
var count = await (s.QueryOver<Person>().ToRowCountQuery().FutureValue<int>().GetValueAsync());
555+
await (tx.CommitAsync());
556+
557+
Assert.That(count, Is.EqualTo(0), "Session wasn't auto flushed.");
558+
}
559+
}
415560
}
416561
}

src/NHibernate.Test/Async/Futures/QueryBatchFixture.cs

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,34 @@ namespace NHibernate.Test.Futures
2727
public class QueryBatchFixtureAsync : TestCaseMappingByCode
2828
{
2929
private Guid _parentId;
30+
private Guid _eagerId;
31+
32+
[Test]
33+
public async Task CanCombineCriteriaAndHqlInFutureAsync()
34+
{
35+
using (var sqlLog = new SqlLogSpy())
36+
using (var session = OpenSession())
37+
{
38+
var future1 = session.QueryOver<EntityComplex>()
39+
.Where(x => x.Version >= 0)
40+
.TransformUsing(new ListTransformerToInt()).Future<int>();
41+
42+
var future2 = session.Query<EntityComplex>().Where(ec => ec.Version > 2).ToFuture();
43+
var future3 = session.Query<EntitySimpleChild>().Select(sc => sc.Name).ToFuture();
44+
45+
var future4 = session
46+
.Query<EntitySimpleChild>()
47+
.ToFutureValue(sc => sc.FirstOrDefault());
48+
49+
Assert.That((await (future1.GetEnumerableAsync())).Count(), Is.GreaterThan(0), "Empty results are not expected");
50+
Assert.That((await (future2.GetEnumerableAsync())).Count(), Is.EqualTo(0), "This query should not return results");
51+
Assert.That((await (future3.GetEnumerableAsync())).Count(), Is.GreaterThan(1), "Empty results are not expected");
52+
Assert.That(await (future4.GetValueAsync()), Is.Not.Null, "Loaded entity should not be null");
53+
54+
if (SupportsMultipleQueries)
55+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1));
56+
}
57+
}
3058

3159
[Test]
3260
public async Task CanCombineCriteriaAndHqlInBatchAsync()
@@ -63,6 +91,44 @@ public async Task CanCombineCriteriaAndHqlInBatchAsync()
6391
}
6492
}
6593

94+
[Test]
95+
public async Task CanCombineCriteriaAndHqlInBatchAsFutureAsync()
96+
{
97+
using (var session = OpenSession())
98+
{
99+
var batch = session
100+
.CreateQueryBatch();
101+
102+
var future1 = batch.AddAsFuture<int>(
103+
session
104+
.QueryOver<EntityComplex>()
105+
.Where(x => x.Version >= 0)
106+
.TransformUsing(new ListTransformerToInt()));
107+
108+
var future2 = batch.AddAsFutureValue<Guid>(session.QueryOver<EntityComplex>().Where(x => x.Version >= 1).Select(x => x.Id));
109+
110+
var future3 = batch.AddAsFuture(session.Query<EntityComplex>().Where(ec => ec.Version > 2));
111+
var future4 = batch.AddAsFutureValue(session.Query<EntityComplex>().Where(ec => ec.Version > 2), ec => ec.FirstOrDefault());
112+
113+
var future5 = batch.AddAsFuture<EntitySimpleChild>(
114+
session.CreateSQLQuery(
115+
$"select * from {nameof(EntitySimpleChild)}")
116+
.AddEntity(typeof(EntitySimpleChild)));
117+
118+
using (var sqlLog = new SqlLogSpy())
119+
{
120+
var future1List = (await (future1.GetEnumerableAsync())).ToList();
121+
var future2Value = await (future2.GetValueAsync());
122+
var future3List = (await (future3.GetEnumerableAsync())).ToList();
123+
var future4Value = await (future4.GetValueAsync());
124+
var future5List = (await (future5.GetEnumerableAsync())).ToList();
125+
126+
if (SupportsMultipleQueries)
127+
Assert.That(sqlLog.Appender.GetEvents().Length, Is.EqualTo(1));
128+
}
129+
}
130+
}
131+
66132
[Test]
67133
public async Task CanFetchCollectionInBatchAsync()
68134
{
@@ -119,6 +185,30 @@ public async Task AfterLoadCallbackAsync()
119185
}
120186
}
121187

188+
//NH-3350 (Duplicate records using Future())
189+
[Test]
190+
public async Task SameCollectionFetchesAsync()
191+
{
192+
using (var session = OpenSession())
193+
{
194+
var entiyComplex = session.QueryOver<EntityComplex>().Where(c => c.Id == _parentId).FutureValue();
195+
196+
session.QueryOver<EntityComplex>()
197+
.Fetch(SelectMode.Fetch, ec => ec.ChildrenList)
198+
.Where(c => c.Id == _parentId).Future();
199+
200+
session.QueryOver<EntityComplex>()
201+
.Fetch(SelectMode.Fetch, ec => ec.ChildrenList)
202+
.Where(c => c.Id == _parentId).Future();
203+
204+
var parent = await (entiyComplex.GetValueAsync());
205+
Assert.That(NHibernateUtil.IsInitialized(parent), Is.True);
206+
Assert.That(NHibernateUtil.IsInitialized(parent.ChildrenList), Is.True);
207+
Assert.That(parent.ChildrenList.Count, Is.EqualTo(2));
208+
209+
}
210+
}
211+
122212
#region Test Setup
123213

124214
protected override HbmMapping GetMappings()
@@ -264,6 +354,7 @@ protected override void OnSetUp()
264354
transaction.Commit();
265355

266356
_parentId = complex.Id;
357+
_eagerId = eager.Id;
267358
}
268359
}
269360

src/NHibernate.Test/Async/NHSpecificTest/NH1989/Fixture.cs

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -163,7 +163,6 @@ public async Task SecondLevelCacheWithMixedCacheableAndNonCacheableFutureAsync()
163163
.SetCacheable(true)
164164
.FutureValue<User>();
165165

166-
// non cacheable Future causes batch to be non-cacheable
167166
int count =
168167
await (s.CreateCriteria<User>()
169168
.SetProjection(Projections.RowCount())
@@ -190,7 +189,9 @@ public async Task SecondLevelCacheWithMixedCacheableAndNonCacheableFutureAsync()
190189
.FutureValue<int>()
191190
.GetValueAsync());
192191

193-
Assert.That(await (userFuture.GetValueAsync()), Is.Null,
192+
Assert.That(await (userFuture.GetValueAsync()), Is.Not.Null,
193+
"query results should come from cache");
194+
Assert.That(count, Is.EqualTo(0),
194195
"query results should not come from cache");
195196
}
196197
}
@@ -216,7 +217,6 @@ public async Task SecondLevelCacheWithMixedCacheRegionsFutureAsync()
216217
.SetCacheRegion("region1")
217218
.FutureValue<User>();
218219

219-
// different cache-region causes batch to be non-cacheable
220220
int count =
221221
await (s.CreateCriteria<User>()
222222
.SetProjection(Projections.RowCount())
@@ -240,6 +240,13 @@ public async Task SecondLevelCacheWithMixedCacheRegionsFutureAsync()
240240
.SetCacheRegion("region1")
241241
.FutureValue<User>();
242242

243+
IFutureValue<User> userFutureWrongRegion =
244+
s.CreateCriteria<User>()
245+
.Add(Restrictions.NaturalId().Set("Name", "test"))
246+
.SetCacheable(true)
247+
.SetCacheRegion("region2")
248+
.FutureValue<User>();
249+
243250
int count =
244251
await (s.CreateCriteria<User>()
245252
.SetProjection(Projections.RowCount())
@@ -248,8 +255,23 @@ public async Task SecondLevelCacheWithMixedCacheRegionsFutureAsync()
248255
.FutureValue<int>()
249256
.GetValueAsync());
250257

251-
Assert.That(await (userFuture.GetValueAsync()), Is.Null,
252-
"query results should not come from cache");
258+
int countWrongRegion =
259+
await (s.CreateCriteria<User>()
260+
.SetProjection(Projections.RowCount())
261+
.SetCacheable(true)
262+
.SetCacheRegion("region1")
263+
.FutureValue<int>()
264+
.GetValueAsync());
265+
266+
Assert.That(await (userFuture.GetValueAsync()), Is.Not.Null,
267+
"query results should come from cache");
268+
Assert.That(count, Is.EqualTo(1),
269+
"query results should come from cache");
270+
271+
Assert.That(await (userFutureWrongRegion.GetValueAsync()), Is.Null,
272+
"query results from wrong cache region");
273+
Assert.That(countWrongRegion, Is.EqualTo(0),
274+
"query results from wrong cache region");
253275
}
254276
}
255277

0 commit comments

Comments
 (0)