Skip to content

Commit f7080df

Browse files
Support CacheMode in QueryBatch (#1796)
And add a test for ReadOnly
1 parent 6e1c370 commit f7080df

15 files changed

+523
-217
lines changed

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

+111
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,116 @@ public async Task UsingHqlToFutureWithCacheAndTransformerDoesntThrowAsync()
388388
}
389389
}
390390

391+
[Test]
392+
public async Task ReadOnlyWorksWithFutureAsync()
393+
{
394+
using (var s = OpenSession())
395+
using (var t = s.BeginTransaction())
396+
{
397+
var futureSimples =
398+
s
399+
.CreateQuery("from EntitySimpleChild")
400+
.SetReadOnly(true)
401+
.Future<EntitySimpleChild>();
402+
var futureSubselect =
403+
s
404+
.CreateQuery("from EntitySubselectChild")
405+
.Future<EntitySubselectChild>();
406+
407+
var simples = (await (futureSimples.GetEnumerableAsync())).ToList();
408+
Assert.That(simples, Has.Count.GreaterThan(0));
409+
foreach (var entity in simples)
410+
{
411+
Assert.That(s.IsReadOnly(entity), Is.True, entity.Name);
412+
}
413+
414+
var subselect = (await (futureSubselect.GetEnumerableAsync())).ToList();
415+
Assert.That(subselect, Has.Count.GreaterThan(0));
416+
foreach (var entity in subselect)
417+
{
418+
Assert.That(s.IsReadOnly(entity), Is.False, entity.Name);
419+
}
420+
421+
await (t.CommitAsync());
422+
}
423+
}
424+
425+
[Test]
426+
public async Task CacheModeWorksWithFutureAsync()
427+
{
428+
Sfi.Statistics.IsStatisticsEnabled = true;
429+
430+
using (var s = OpenSession())
431+
using (var t = s.BeginTransaction())
432+
{
433+
s
434+
.CreateQuery("from EntitySimpleChild")
435+
.SetCacheable(true)
436+
.SetCacheMode(CacheMode.Get)
437+
.Future<EntitySimpleChild>();
438+
s
439+
.CreateQuery("from EntityComplex")
440+
.SetCacheable(true)
441+
.SetCacheMode(CacheMode.Put)
442+
.Future<EntityComplex>();
443+
await (s
444+
.CreateQuery("from EntitySubselectChild")
445+
.SetCacheable(true)
446+
.Future<EntitySubselectChild>()
447+
.GetEnumerableAsync());
448+
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Future put");
449+
450+
await (t.CommitAsync());
451+
}
452+
453+
using (var s = OpenSession())
454+
using (var t = s.BeginTransaction())
455+
{
456+
Sfi.Statistics.Clear();
457+
await (s
458+
.CreateQuery("from EntitySimpleChild")
459+
.SetCacheable(true)
460+
.ListAsync<EntitySimpleChild>());
461+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "EntitySimpleChild query hit");
462+
463+
Sfi.Statistics.Clear();
464+
await (s
465+
.CreateQuery("from EntityComplex")
466+
.SetCacheable(true)
467+
.ListAsync<EntityComplex>());
468+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntityComplex query hit");
469+
470+
Sfi.Statistics.Clear();
471+
await (s
472+
.CreateQuery("from EntitySubselectChild")
473+
.SetCacheable(true)
474+
.ListAsync<EntitySubselectChild>());
475+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntitySubselectChild query hit");
476+
477+
await (t.CommitAsync());
478+
}
479+
480+
using (var s = OpenSession())
481+
using (var t = s.BeginTransaction())
482+
{
483+
Sfi.Statistics.Clear();
484+
s
485+
.CreateQuery("from EntitySimpleChild")
486+
.SetCacheable(true)
487+
.SetCacheMode(CacheMode.Get)
488+
.Future<EntitySimpleChild>();
489+
await (s
490+
.CreateQuery("from EntitySubselectChild")
491+
.SetCacheable(true)
492+
.Future<EntitySubselectChild>()
493+
.GetEnumerableAsync());
494+
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Second future put");
495+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Second future hit");
496+
497+
await (t.CommitAsync());
498+
}
499+
}
500+
391501
#region Test Setup
392502

393503
protected override HbmMapping GetMappings()
@@ -471,6 +581,7 @@ protected override void OnTearDown()
471581
session.Flush();
472582
transaction.Commit();
473583
}
584+
Sfi.Statistics.IsStatisticsEnabled = false;
474585
}
475586

476587
protected override void OnSetUp()

src/NHibernate.Test/Futures/QueryBatchFixture.cs

+111
Original file line numberDiff line numberDiff line change
@@ -376,6 +376,116 @@ public void UsingHqlToFutureWithCacheAndTransformerDoesntThrow()
376376
}
377377
}
378378

379+
[Test]
380+
public void ReadOnlyWorksWithFuture()
381+
{
382+
using (var s = OpenSession())
383+
using (var t = s.BeginTransaction())
384+
{
385+
var futureSimples =
386+
s
387+
.CreateQuery("from EntitySimpleChild")
388+
.SetReadOnly(true)
389+
.Future<EntitySimpleChild>();
390+
var futureSubselect =
391+
s
392+
.CreateQuery("from EntitySubselectChild")
393+
.Future<EntitySubselectChild>();
394+
395+
var simples = futureSimples.GetEnumerable().ToList();
396+
Assert.That(simples, Has.Count.GreaterThan(0));
397+
foreach (var entity in simples)
398+
{
399+
Assert.That(s.IsReadOnly(entity), Is.True, entity.Name);
400+
}
401+
402+
var subselect = futureSubselect.GetEnumerable().ToList();
403+
Assert.That(subselect, Has.Count.GreaterThan(0));
404+
foreach (var entity in subselect)
405+
{
406+
Assert.That(s.IsReadOnly(entity), Is.False, entity.Name);
407+
}
408+
409+
t.Commit();
410+
}
411+
}
412+
413+
[Test]
414+
public void CacheModeWorksWithFuture()
415+
{
416+
Sfi.Statistics.IsStatisticsEnabled = true;
417+
418+
using (var s = OpenSession())
419+
using (var t = s.BeginTransaction())
420+
{
421+
s
422+
.CreateQuery("from EntitySimpleChild")
423+
.SetCacheable(true)
424+
.SetCacheMode(CacheMode.Get)
425+
.Future<EntitySimpleChild>();
426+
s
427+
.CreateQuery("from EntityComplex")
428+
.SetCacheable(true)
429+
.SetCacheMode(CacheMode.Put)
430+
.Future<EntityComplex>();
431+
s
432+
.CreateQuery("from EntitySubselectChild")
433+
.SetCacheable(true)
434+
.Future<EntitySubselectChild>()
435+
.GetEnumerable();
436+
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(2), "Future put");
437+
438+
t.Commit();
439+
}
440+
441+
using (var s = OpenSession())
442+
using (var t = s.BeginTransaction())
443+
{
444+
Sfi.Statistics.Clear();
445+
s
446+
.CreateQuery("from EntitySimpleChild")
447+
.SetCacheable(true)
448+
.List<EntitySimpleChild>();
449+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(0), "EntitySimpleChild query hit");
450+
451+
Sfi.Statistics.Clear();
452+
s
453+
.CreateQuery("from EntityComplex")
454+
.SetCacheable(true)
455+
.List<EntityComplex>();
456+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntityComplex query hit");
457+
458+
Sfi.Statistics.Clear();
459+
s
460+
.CreateQuery("from EntitySubselectChild")
461+
.SetCacheable(true)
462+
.List<EntitySubselectChild>();
463+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(1), "EntitySubselectChild query hit");
464+
465+
t.Commit();
466+
}
467+
468+
using (var s = OpenSession())
469+
using (var t = s.BeginTransaction())
470+
{
471+
Sfi.Statistics.Clear();
472+
s
473+
.CreateQuery("from EntitySimpleChild")
474+
.SetCacheable(true)
475+
.SetCacheMode(CacheMode.Get)
476+
.Future<EntitySimpleChild>();
477+
s
478+
.CreateQuery("from EntitySubselectChild")
479+
.SetCacheable(true)
480+
.Future<EntitySubselectChild>()
481+
.GetEnumerable();
482+
Assert.That(Sfi.Statistics.QueryCachePutCount, Is.EqualTo(0), "Second future put");
483+
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(2), "Second future hit");
484+
485+
t.Commit();
486+
}
487+
}
488+
379489
#region Test Setup
380490

381491
protected override HbmMapping GetMappings()
@@ -459,6 +569,7 @@ protected override void OnTearDown()
459569
session.Flush();
460570
transaction.Commit();
461571
}
572+
Sfi.Statistics.IsStatisticsEnabled = false;
462573
}
463574

464575
protected override void OnSetUp()

src/NHibernate/Async/Cache/StandardQueryCache.cs

+16-6
Original file line numberDiff line numberDiff line change
@@ -245,13 +245,23 @@ public async Task<IList[]> GetManyAsync(
245245
else
246246
queryParams.ReadOnly = persistenceContext.DefaultReadOnly;
247247

248-
try
248+
// Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause
249+
// entity loads, which may interact with the cache.
250+
using (session.SwitchCacheMode(queryParams.CacheMode))
249251
{
250-
results[i] = await (GetResultFromCacheableAsync(key, returnTypes[i], queryParams.NaturalKeyLookup, session, cacheable, cancellationToken)).ConfigureAwait(false);
251-
}
252-
finally
253-
{
254-
persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
252+
try
253+
{
254+
results[i] = await (GetResultFromCacheableAsync(
255+
key,
256+
returnTypes[i],
257+
queryParams.NaturalKeyLookup,
258+
session,
259+
cacheable, cancellationToken)).ConfigureAwait(false);
260+
}
261+
finally
262+
{
263+
persistenceContext.DefaultReadOnly = defaultReadOnlyOrig;
264+
}
255265
}
256266
}
257267

src/NHibernate/Async/Event/Default/DefaultDeleteEventListener.cs

+28-26
Original file line numberDiff line numberDiff line change
@@ -222,40 +222,42 @@ protected virtual async Task DeleteEntityAsync(IEventSource session, object enti
222222
protected virtual async Task CascadeBeforeDeleteAsync(IEventSource session, IEntityPersister persister, object entity, EntityEntry entityEntry, ISet<object> transientEntities, CancellationToken cancellationToken)
223223
{
224224
cancellationToken.ThrowIfCancellationRequested();
225-
ISessionImplementor si = session;
226-
CacheMode cacheMode = si.CacheMode;
227-
si.CacheMode = CacheMode.Get;
228-
session.PersistenceContext.IncrementCascadeLevel();
229-
try
225+
using (session.SwitchCacheMode(CacheMode.Get))
230226
{
231-
// cascade-delete to collections BEFORE the collection owner is deleted
232-
await (new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOnAsync(persister, entity,
233-
transientEntities, cancellationToken)).ConfigureAwait(false);
234-
}
235-
finally
236-
{
237-
session.PersistenceContext.DecrementCascadeLevel();
238-
si.CacheMode = cacheMode;
227+
session.PersistenceContext.IncrementCascadeLevel();
228+
try
229+
{
230+
// cascade-delete to collections BEFORE the collection owner is deleted
231+
await (new Cascade(CascadingAction.Delete, CascadePoint.AfterInsertBeforeDelete, session).CascadeOnAsync(
232+
persister,
233+
entity,
234+
transientEntities, cancellationToken)).ConfigureAwait(false);
235+
}
236+
finally
237+
{
238+
session.PersistenceContext.DecrementCascadeLevel();
239+
}
239240
}
240241
}
241242

242243
protected virtual async Task CascadeAfterDeleteAsync(IEventSource session, IEntityPersister persister, object entity, ISet<object> transientEntities, CancellationToken cancellationToken)
243244
{
244245
cancellationToken.ThrowIfCancellationRequested();
245-
ISessionImplementor si = session;
246-
CacheMode cacheMode = si.CacheMode;
247-
si.CacheMode = CacheMode.Get;
248-
session.PersistenceContext.IncrementCascadeLevel();
249-
try
246+
using (session.SwitchCacheMode(CacheMode.Get))
250247
{
251-
// cascade-delete to many-to-one AFTER the parent was deleted
252-
await (new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOnAsync(persister, entity,
253-
transientEntities, cancellationToken)).ConfigureAwait(false);
254-
}
255-
finally
256-
{
257-
session.PersistenceContext.DecrementCascadeLevel();
258-
si.CacheMode = cacheMode;
248+
session.PersistenceContext.IncrementCascadeLevel();
249+
try
250+
{
251+
// cascade-delete to many-to-one AFTER the parent was deleted
252+
await (new Cascade(CascadingAction.Delete, CascadePoint.BeforeInsertAfterDelete, session).CascadeOnAsync(
253+
persister,
254+
entity,
255+
transientEntities, cancellationToken)).ConfigureAwait(false);
256+
}
257+
finally
258+
{
259+
session.PersistenceContext.DecrementCascadeLevel();
260+
}
259261
}
260262
}
261263
}

src/NHibernate/Async/Loader/Loader.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -1227,7 +1227,7 @@ private async Task<IList> GetResultFromQueryCacheAsync(
12271227
IQueryCache queryCache, QueryKey key, CancellationToken cancellationToken)
12281228
{
12291229
cancellationToken.ThrowIfCancellationRequested();
1230-
if (!CanGetFromCache(session, queryParameters))
1230+
if (!queryParameters.CanGetFromCache(session))
12311231
return null;
12321232

12331233
var result = await (queryCache.GetAsync(
@@ -1256,7 +1256,7 @@ private async Task PutResultInQueryCacheAsync(ISessionImplementor session, Query
12561256
IQueryCache queryCache, QueryKey key, IList result, CancellationToken cancellationToken)
12571257
{
12581258
cancellationToken.ThrowIfCancellationRequested();
1259-
if (!session.CacheMode.HasFlag(CacheMode.Put))
1259+
if (!queryParameters.CanPutToCache(session))
12601260
return;
12611261

12621262
var put = await (queryCache.PutAsync(

0 commit comments

Comments
 (0)