13
13
using System . Collections . Generic ;
14
14
using System . Linq ;
15
15
using NHibernate . Cfg ;
16
+ using NHibernate . Collection ;
16
17
using NHibernate . Engine ;
17
18
using NHibernate . Persister . Collection ;
18
19
using NHibernate . Type ;
@@ -208,44 +209,86 @@ public async Task<IList[]> GetManyAsync(
208
209
var persistenceContext = session . PersistenceContext ;
209
210
var defaultReadOnlyOrig = persistenceContext . DefaultReadOnly ;
210
211
var results = new IList [ keys . Length ] ;
211
- for ( var i = 0 ; i < keys . Length ; i ++ )
212
+ var finalReturnTypes = new ICacheAssembler [ keys . Length ] [ ] ;
213
+ try
212
214
{
213
- var cacheable = cacheables [ i ] ;
214
- if ( cacheable == null )
215
- continue ;
215
+ session . PersistenceContext . BatchFetchQueue . InitializeQueryCacheQueue ( ) ;
216
216
217
- var key = keys [ i ] ;
218
- if ( checkedSpacesIndexes . Contains ( i ) && ! upToDates [ upToDatesIndex ++ ] )
217
+ for ( var i = 0 ; i < keys . Length ; i ++ )
219
218
{
220
- Log . Debug ( "cached query results were not up to date for: {0}" , key ) ;
221
- continue ;
219
+ var cacheable = cacheables [ i ] ;
220
+ if ( cacheable == null )
221
+ continue ;
222
+
223
+ var key = keys [ i ] ;
224
+ if ( checkedSpacesIndexes . Contains ( i ) && ! upToDates [ upToDatesIndex ++ ] )
225
+ {
226
+ Log . Debug ( "cached query results were not up to date for: {0}" , key ) ;
227
+ continue ;
228
+ }
229
+
230
+ var queryParams = queryParameters [ i ] ;
231
+ if ( queryParams . IsReadOnlyInitialized )
232
+ persistenceContext . DefaultReadOnly = queryParams . ReadOnly ;
233
+ else
234
+ queryParams . ReadOnly = persistenceContext . DefaultReadOnly ;
235
+
236
+ Log . Debug ( "returning cached query results for: {0}" , key ) ;
237
+
238
+ finalReturnTypes [ i ] = GetReturnTypes ( key , returnTypes [ i ] , cacheable ) ;
239
+ await ( PerformBeforeAssembleAsync ( finalReturnTypes [ i ] , session , cacheable , cancellationToken ) ) . ConfigureAwait ( false ) ;
222
240
}
223
241
224
- var queryParams = queryParameters [ i ] ;
225
- if ( queryParams . IsReadOnlyInitialized )
226
- persistenceContext . DefaultReadOnly = queryParams . ReadOnly ;
227
- else
228
- queryParams . ReadOnly = persistenceContext . DefaultReadOnly ;
242
+ for ( var i = 0 ; i < keys . Length ; i ++ )
243
+ {
244
+ if ( finalReturnTypes [ i ] == null )
245
+ {
246
+ continue ;
247
+ }
229
248
230
- // Adjust the session cache mode, as GetResultFromCacheable assemble types which may cause
231
- // entity loads, which may interact with the cache.
232
- using ( session . SwitchCacheMode ( queryParams . CacheMode ) )
249
+ var queryParams = queryParameters [ i ] ;
250
+ // Adjust the session cache mode, as PerformAssemble assemble types which may cause
251
+ // entity loads, which may interact with the cache.
252
+ using ( session . SwitchCacheMode ( queryParams . CacheMode ) )
253
+ {
254
+ try
255
+ {
256
+ results [ i ] = await ( PerformAssembleAsync ( keys [ i ] , finalReturnTypes [ i ] , queryParams . NaturalKeyLookup , session , cacheables [ i ] , cancellationToken ) ) . ConfigureAwait ( false ) ;
257
+ }
258
+ finally
259
+ {
260
+ persistenceContext . DefaultReadOnly = defaultReadOnlyOrig ;
261
+ }
262
+ }
263
+ }
264
+
265
+ for ( var i = 0 ; i < keys . Length ; i ++ )
233
266
{
234
- try
267
+ if ( finalReturnTypes [ i ] == null )
235
268
{
236
- results [ i ] = await ( GetResultFromCacheableAsync (
237
- key ,
238
- returnTypes [ i ] ,
239
- queryParams . NaturalKeyLookup ,
240
- session ,
241
- cacheable , cancellationToken ) ) . ConfigureAwait ( false ) ;
269
+ continue ;
242
270
}
243
- finally
271
+
272
+ var queryParams = queryParameters [ i ] ;
273
+ // Adjust the session cache mode, as InitializeCollections will initialize collections,
274
+ // which may interact with the cache.
275
+ using ( session . SwitchCacheMode ( queryParams . CacheMode ) )
244
276
{
245
- persistenceContext . DefaultReadOnly = defaultReadOnlyOrig ;
277
+ try
278
+ {
279
+ await ( InitializeCollectionsAsync ( finalReturnTypes [ i ] , session , results [ i ] , cacheables [ i ] , cancellationToken ) ) . ConfigureAwait ( false ) ;
280
+ }
281
+ finally
282
+ {
283
+ persistenceContext . DefaultReadOnly = defaultReadOnlyOrig ;
284
+ }
246
285
}
247
286
}
248
287
}
288
+ finally
289
+ {
290
+ session . PersistenceContext . BatchFetchQueue . TerminateQueryCacheQueue ( ) ;
291
+ }
249
292
250
293
return results ;
251
294
}
@@ -275,20 +318,40 @@ private static async Task<List<object>> GetCacheableResultAsync(
275
318
return cacheable ;
276
319
}
277
320
278
- private async Task < IList > GetResultFromCacheableAsync (
279
- QueryKey key ,
321
+ private static async Task PerformBeforeAssembleAsync (
280
322
ICacheAssembler [ ] returnTypes ,
281
- bool isNaturalKeyLookup ,
282
323
ISessionImplementor session ,
283
324
IList cacheable , CancellationToken cancellationToken )
284
325
{
285
326
cancellationToken . ThrowIfCancellationRequested ( ) ;
286
- Log . Debug ( "returning cached query results for: {0}" , key ) ;
287
- if ( key . ResultTransformer ? . AutoDiscoverTypes == true && cacheable . Count > 0 )
327
+ if ( returnTypes . Length == 1 )
288
328
{
289
- returnTypes = GuessTypes ( cacheable ) ;
329
+ var returnType = returnTypes [ 0 ] ;
330
+
331
+ // Skip first element, it is the timestamp
332
+ for ( var i = 1 ; i < cacheable . Count ; i ++ )
333
+ {
334
+ await ( returnType . BeforeAssembleAsync ( cacheable [ i ] , session , cancellationToken ) ) . ConfigureAwait ( false ) ;
335
+ }
290
336
}
337
+ else
338
+ {
339
+ // Skip first element, it is the timestamp
340
+ for ( var i = 1 ; i < cacheable . Count ; i ++ )
341
+ {
342
+ await ( TypeHelper . BeforeAssembleAsync ( ( object [ ] ) cacheable [ i ] , returnTypes , session , cancellationToken ) ) . ConfigureAwait ( false ) ;
343
+ }
344
+ }
345
+ }
291
346
347
+ private async Task < IList > PerformAssembleAsync (
348
+ QueryKey key ,
349
+ ICacheAssembler [ ] returnTypes ,
350
+ bool isNaturalKeyLookup ,
351
+ ISessionImplementor session ,
352
+ IList cacheable , CancellationToken cancellationToken )
353
+ {
354
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
292
355
try
293
356
{
294
357
var result = new List < object > ( cacheable . Count - 1 ) ;
@@ -297,53 +360,27 @@ private async Task<IList> GetResultFromCacheableAsync(
297
360
var returnType = returnTypes [ 0 ] ;
298
361
299
362
// Skip first element, it is the timestamp
300
- for ( var i = 1 ; i < cacheable . Count ; i ++ )
301
- {
302
- await ( returnType . BeforeAssembleAsync ( cacheable [ i ] , session , cancellationToken ) ) . ConfigureAwait ( false ) ;
303
- }
304
-
305
363
for ( var i = 1 ; i < cacheable . Count ; i ++ )
306
364
{
307
365
result . Add ( await ( returnType . AssembleAsync ( cacheable [ i ] , session , null , cancellationToken ) ) . ConfigureAwait ( false ) ) ;
308
366
}
309
367
}
310
368
else
311
369
{
312
- var collectionIndexes = new Dictionary < int , ICollectionPersister > ( ) ;
313
370
var nonCollectionTypeIndexes = new List < int > ( ) ;
314
371
for ( var i = 0 ; i < returnTypes . Length ; i ++ )
315
372
{
316
- if ( returnTypes [ i ] is CollectionType collectionType )
317
- {
318
- collectionIndexes . Add ( i , session . Factory . GetCollectionPersister ( collectionType . Role ) ) ;
319
- }
320
- else
373
+ if ( ! ( returnTypes [ i ] is CollectionType ) )
321
374
{
322
375
nonCollectionTypeIndexes . Add ( i ) ;
323
376
}
324
377
}
325
378
326
379
// Skip first element, it is the timestamp
327
- for ( var i = 1 ; i < cacheable . Count ; i ++ )
328
- {
329
- await ( TypeHelper . BeforeAssembleAsync ( ( object [ ] ) cacheable [ i ] , returnTypes , session , cancellationToken ) ) . ConfigureAwait ( false ) ;
330
- }
331
-
332
380
for ( var i = 1 ; i < cacheable . Count ; i ++ )
333
381
{
334
382
result . Add ( await ( TypeHelper . AssembleAsync ( ( object [ ] ) cacheable [ i ] , returnTypes , nonCollectionTypeIndexes , session , cancellationToken ) ) . ConfigureAwait ( false ) ) ;
335
383
}
336
-
337
- // Initialization of the fetched collection must be done at the end in order to be able to batch fetch them
338
- // from the cache or database. The collections were already created in the previous for statement so we only
339
- // have to initialize them.
340
- if ( collectionIndexes . Count > 0 )
341
- {
342
- for ( var i = 1 ; i < cacheable . Count ; i ++ )
343
- {
344
- await ( TypeHelper . InitializeCollectionsAsync ( ( object [ ] ) cacheable [ i ] , ( object [ ] ) result [ i - 1 ] , collectionIndexes , session , cancellationToken ) ) . ConfigureAwait ( false ) ;
345
- }
346
- }
347
384
}
348
385
349
386
return result ;
@@ -367,6 +404,66 @@ private async Task<IList> GetResultFromCacheableAsync(
367
404
}
368
405
}
369
406
407
+ private static async Task InitializeCollectionsAsync (
408
+ ICacheAssembler [ ] returnTypes ,
409
+ ISessionImplementor session ,
410
+ IList assembleResult ,
411
+ IList cacheResult , CancellationToken cancellationToken )
412
+ {
413
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
414
+ var collectionIndexes = new Dictionary < int , ICollectionPersister > ( ) ;
415
+ for ( var i = 0 ; i < returnTypes . Length ; i ++ )
416
+ {
417
+ if ( returnTypes [ i ] is CollectionType collectionType )
418
+ {
419
+ collectionIndexes . Add ( i , session . Factory . GetCollectionPersister ( collectionType . Role ) ) ;
420
+ }
421
+ }
422
+
423
+ if ( collectionIndexes . Count == 0 )
424
+ {
425
+ return ;
426
+ }
427
+
428
+ // Skip first element, it is the timestamp
429
+ for ( var i = 1 ; i < cacheResult . Count ; i ++ )
430
+ {
431
+ // Initialization of the fetched collection must be done at the end in order to be able to batch fetch them
432
+ // from the cache or database. The collections were already created when their owners were assembled so we only
433
+ // have to initialize them.
434
+ await ( TypeHelper . InitializeCollectionsAsync (
435
+ ( object [ ] ) cacheResult [ i ] ,
436
+ ( object [ ] ) assembleResult [ i - 1 ] ,
437
+ collectionIndexes ,
438
+ session , cancellationToken ) ) . ConfigureAwait ( false ) ;
439
+ }
440
+ }
441
+
442
+ private async Task < IList > GetResultFromCacheableAsync (
443
+ QueryKey key ,
444
+ ICacheAssembler [ ] returnTypes ,
445
+ bool isNaturalKeyLookup ,
446
+ ISessionImplementor session ,
447
+ IList cacheable , CancellationToken cancellationToken )
448
+ {
449
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
450
+ Log . Debug ( "returning cached query results for: {0}" , key ) ;
451
+ returnTypes = GetReturnTypes ( key , returnTypes , cacheable ) ;
452
+ try
453
+ {
454
+ session . PersistenceContext . BatchFetchQueue . InitializeQueryCacheQueue ( ) ;
455
+
456
+ await ( PerformBeforeAssembleAsync ( returnTypes , session , cacheable , cancellationToken ) ) . ConfigureAwait ( false ) ;
457
+ var result = await ( PerformAssembleAsync ( key , returnTypes , isNaturalKeyLookup , session , cacheable , cancellationToken ) ) . ConfigureAwait ( false ) ;
458
+ await ( InitializeCollectionsAsync ( returnTypes , session , result , cacheable , cancellationToken ) ) . ConfigureAwait ( false ) ;
459
+ return result ;
460
+ }
461
+ finally
462
+ {
463
+ session . PersistenceContext . BatchFetchQueue . TerminateQueryCacheQueue ( ) ;
464
+ }
465
+ }
466
+
370
467
protected virtual Task < bool > IsUpToDateAsync ( ISet < string > spaces , long timestamp , CancellationToken cancellationToken )
371
468
{
372
469
if ( cancellationToken . IsCancellationRequested )
0 commit comments