Skip to content

Commit f3bd74e

Browse files
authored
Call BeforeAssemble on persistent collection InitializeFromCache to allow batch fetching (#3365)
Fixes #3359
1 parent 9302ad4 commit f3bd74e

14 files changed

+156
-19
lines changed

src/NHibernate.Test/Async/CacheTest/BatchableCacheFixture.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1565,8 +1565,31 @@ public async Task QueryFetchEntityBatchCacheTestAsync(bool clearEntityCacheAfter
15651565
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
15661566
}
15671567

1568+
[Test]
1569+
public async Task CollectionLazyInitializationFromCacheIsBatchedAsync()
1570+
{
1571+
using (var s = OpenSession())
1572+
{
1573+
var readOnly = await (s.GetAsync<ReadOnly>(await (s.Query<ReadOnly>().Select(x => x.Id).FirstAsync())));
1574+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1575+
}
1576+
1577+
var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
1578+
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
1579+
itemCache.ClearStatistics();
1580+
1581+
using (var s = OpenSession())
1582+
{
1583+
var readOnly = await (s.GetAsync<ReadOnly>(await (s.Query<ReadOnly>().Select(x => x.Id).FirstAsync())));
1584+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1585+
}
1586+
1587+
// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
1588+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
1589+
}
1590+
15681591
private async Task AssertMultipleCacheCallsAsync<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
1569-
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken))
1592+
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null, CancellationToken cancellationToken = default(CancellationToken))
15701593
where TEntity : CacheEntity
15711594
{
15721595
var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName);

src/NHibernate.Test/CacheTest/BatchableCacheFixture.cs

Lines changed: 24 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1553,8 +1553,31 @@ public void QueryFetchEntityBatchCacheTest(bool clearEntityCacheAfterQuery, bool
15531553
Assert.That(Sfi.Statistics.QueryCacheHitCount, Is.EqualTo(future ? 2 : 1), "Unexpected cache hit count");
15541554
}
15551555

1556+
[Test]
1557+
public void CollectionLazyInitializationFromCacheIsBatched()
1558+
{
1559+
using (var s = OpenSession())
1560+
{
1561+
var readOnly = s.Get<ReadOnly>(s.Query<ReadOnly>().Select(x => x.Id).First());
1562+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1563+
}
1564+
1565+
var itemPersister = Sfi.GetEntityPersister(typeof(ReadOnlyItem).FullName);
1566+
var itemCache = (BatchableCache) itemPersister.Cache.Cache;
1567+
itemCache.ClearStatistics();
1568+
1569+
using (var s = OpenSession())
1570+
{
1571+
var readOnly = s.Get<ReadOnly>(s.Query<ReadOnly>().Select(x => x.Id).First());
1572+
Assert.That(readOnly.Items.Count, Is.EqualTo(6));
1573+
}
1574+
1575+
// 6 items with batch-size = 4 so 2 GetMany calls are expected 1st call: 4 items + 2nd call: 2 items
1576+
Assert.That(itemCache.GetMultipleCalls.Count, Is.EqualTo(2));
1577+
}
1578+
15561579
private void AssertMultipleCacheCalls<TEntity>(IEnumerable<int> loadIds, IReadOnlyList<int> getIds, int idIndex,
1557-
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null)
1580+
int[][] fetchedIdIndexes, int[] putIdIndexes, Func<int, bool> cacheBeforeLoadFn = null)
15581581
where TEntity : CacheEntity
15591582
{
15601583
var persister = Sfi.GetEntityPersister(typeof(TEntity).FullName);

src/NHibernate/Async/Collection/Generic/PersistentGenericBag.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,9 +109,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
109109
var array = (object[]) disassembled;
110110
var size = array.Length;
111111
BeforeInitialize(persister, size);
112+
113+
var elementType = persister.ElementType;
114+
for (int i = 0; i < size; i++)
115+
{
116+
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
117+
}
118+
112119
for (var i = 0; i < size; i++)
113120
{
114-
var element = await (persister.ElementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
121+
var element = await (elementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
115122
if (element != null)
116123
{
117124
_gbag.Add((T) element);

src/NHibernate/Async/Collection/Generic/PersistentGenericIdentifierBag.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
4444
object[] array = (object[])disassembled;
4545
int size = array.Length;
4646
BeforeInitialize(persister, size);
47+
48+
var identifierType = persister.IdentifierType;
49+
var elementType = persister.ElementType;
50+
for (int i = 0; i < size; i++)
51+
{
52+
await (identifierType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
53+
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
54+
}
55+
4756
for (int i = 0; i < size; i += 2)
4857
{
49-
_identifiers[i / 2] = await (persister.IdentifierType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
50-
_values.Add((T) await (persister.ElementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false));
58+
_identifiers[i / 2] = await (identifierType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
59+
_values.Add((T) await (elementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false));
5160
}
5261
}
5362

src/NHibernate/Async/Collection/Generic/PersistentGenericList.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
9898
object[] array = (object[])disassembled;
9999
int size = array.Length;
100100
BeforeInitialize(persister, size);
101+
102+
var elementType = persister.ElementType;
101103
for (int i = 0; i < size; i++)
102104
{
103-
var element = await (persister.ElementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
105+
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
106+
}
107+
108+
for (int i = 0; i < size; i++)
109+
{
110+
var element = await (elementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
104111
WrappedList.Add((T) (element ?? DefaultForType));
105112
}
106113
}

src/NHibernate/Async/Collection/Generic/PersistentGenericMap.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,10 +94,19 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
9494
object[] array = (object[])disassembled;
9595
int size = array.Length;
9696
BeforeInitialize(persister, size);
97+
98+
var indexType = persister.IndexType;
99+
var elementType = persister.ElementType;
100+
for (int i = 0; i < size; i++)
101+
{
102+
await (indexType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
103+
await (elementType.BeforeAssembleAsync(array[i + 1], Session, cancellationToken)).ConfigureAwait(false);
104+
}
105+
97106
for (int i = 0; i < size; i += 2)
98107
{
99-
WrappedMap[(TKey)await (persister.IndexType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false)] =
100-
(TValue)await (persister.ElementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false);
108+
WrappedMap[(TKey)await (indexType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false)] =
109+
(TValue)await (elementType.AssembleAsync(array[i + 1], Session, owner, cancellationToken)).ConfigureAwait(false);
101110
}
102111
}
103112

src/NHibernate/Async/Collection/Generic/PersistentGenericSet.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,9 +84,16 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
8484
var array = (object[])disassembled;
8585
int size = array.Length;
8686
BeforeInitialize(persister, size);
87+
88+
var elementType = persister.ElementType;
89+
for (int i = 0; i < size; i++)
90+
{
91+
await (elementType.BeforeAssembleAsync(array[i], Session, cancellationToken)).ConfigureAwait(false);
92+
}
93+
8794
for (int i = 0; i < size; i++)
8895
{
89-
var element = await (persister.ElementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
96+
var element = await (elementType.AssembleAsync(array[i], Session, owner, cancellationToken)).ConfigureAwait(false);
9097
if (element != null)
9198
{
9299
WrappedSet.Add((T) element);

src/NHibernate/Async/Collection/PersistentArrayHolder.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -94,9 +94,15 @@ public override async Task InitializeFromCacheAsync(ICollectionPersister persist
9494

9595
array = System.Array.CreateInstance(persister.ElementClass, cached.Length);
9696

97+
var elementType = persister.ElementType;
9798
for (int i = 0; i < cached.Length; i++)
9899
{
99-
array.SetValue(await (persister.ElementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
100+
await (elementType.BeforeAssembleAsync(cached[i], Session, cancellationToken)).ConfigureAwait(false);
101+
}
102+
103+
for (int i = 0; i < cached.Length; i++)
104+
{
105+
array.SetValue(await (elementType.AssembleAsync(cached[i], Session, owner, cancellationToken)).ConfigureAwait(false), i);
100106
}
101107
}
102108

src/NHibernate/Collection/Generic/PersistentGenericBag.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -400,9 +400,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object
400400
var array = (object[]) disassembled;
401401
var size = array.Length;
402402
BeforeInitialize(persister, size);
403+
404+
var elementType = persister.ElementType;
405+
for (int i = 0; i < size; i++)
406+
{
407+
elementType.BeforeAssemble(array[i], Session);
408+
}
409+
403410
for (var i = 0; i < size; i++)
404411
{
405-
var element = persister.ElementType.Assemble(array[i], Session, owner);
412+
var element = elementType.Assemble(array[i], Session, owner);
406413
if (element != null)
407414
{
408415
_gbag.Add((T) element);

src/NHibernate/Collection/Generic/PersistentGenericIdentifierBag.cs

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,19 @@ public override void InitializeFromCache(ICollectionPersister persister, object
7575
object[] array = (object[])disassembled;
7676
int size = array.Length;
7777
BeforeInitialize(persister, size);
78+
79+
var identifierType = persister.IdentifierType;
80+
var elementType = persister.ElementType;
81+
for (int i = 0; i < size; i++)
82+
{
83+
identifierType.BeforeAssemble(array[i], Session);
84+
elementType.BeforeAssemble(array[i + 1], Session);
85+
}
86+
7887
for (int i = 0; i < size; i += 2)
7988
{
80-
_identifiers[i / 2] = persister.IdentifierType.Assemble(array[i], Session, owner);
81-
_values.Add((T) persister.ElementType.Assemble(array[i + 1], Session, owner));
89+
_identifiers[i / 2] = identifierType.Assemble(array[i], Session, owner);
90+
_values.Add((T) elementType.Assemble(array[i + 1], Session, owner));
8291
}
8392
}
8493

src/NHibernate/Collection/Generic/PersistentGenericList.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,9 +160,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object
160160
object[] array = (object[])disassembled;
161161
int size = array.Length;
162162
BeforeInitialize(persister, size);
163+
164+
var elementType = persister.ElementType;
163165
for (int i = 0; i < size; i++)
164166
{
165-
var element = persister.ElementType.Assemble(array[i], Session, owner);
167+
elementType.BeforeAssemble(array[i], Session);
168+
}
169+
170+
for (int i = 0; i < size; i++)
171+
{
172+
var element = elementType.Assemble(array[i], Session, owner);
166173
WrappedList.Add((T) (element ?? DefaultForType));
167174
}
168175
}

src/NHibernate/Collection/Generic/PersistentGenericMap.cs

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -163,10 +163,19 @@ public override void InitializeFromCache(ICollectionPersister persister, object
163163
object[] array = (object[])disassembled;
164164
int size = array.Length;
165165
BeforeInitialize(persister, size);
166+
167+
var indexType = persister.IndexType;
168+
var elementType = persister.ElementType;
169+
for (int i = 0; i < size; i++)
170+
{
171+
indexType.BeforeAssemble(array[i], Session);
172+
elementType.BeforeAssemble(array[i + 1], Session);
173+
}
174+
166175
for (int i = 0; i < size; i += 2)
167176
{
168-
WrappedMap[(TKey)persister.IndexType.Assemble(array[i], Session, owner)] =
169-
(TValue)persister.ElementType.Assemble(array[i + 1], Session, owner);
177+
WrappedMap[(TKey)indexType.Assemble(array[i], Session, owner)] =
178+
(TValue)elementType.Assemble(array[i + 1], Session, owner);
170179
}
171180
}
172181

@@ -246,8 +255,9 @@ public void Add(TKey key, TValue value)
246255
{
247256
if (key == null)
248257
{
249-
throw new ArgumentNullException("key");
258+
throw new ArgumentNullException(nameof(key));
250259
}
260+
251261
if (PutQueueEnabled)
252262
{
253263
var found = TryReadElementByKey<TKey, TValue>(key, out _, out _);

src/NHibernate/Collection/Generic/PersistentGenericSet.cs

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,16 @@ public override void InitializeFromCache(ICollectionPersister persister, object
154154
var array = (object[])disassembled;
155155
int size = array.Length;
156156
BeforeInitialize(persister, size);
157+
158+
var elementType = persister.ElementType;
159+
for (int i = 0; i < size; i++)
160+
{
161+
elementType.BeforeAssemble(array[i], Session);
162+
}
163+
157164
for (int i = 0; i < size; i++)
158165
{
159-
var element = persister.ElementType.Assemble(array[i], Session, owner);
166+
var element = elementType.Assemble(array[i], Session, owner);
160167
if (element != null)
161168
{
162169
WrappedSet.Add((T) element);

src/NHibernate/Collection/PersistentArrayHolder.cs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -196,9 +196,15 @@ public override void InitializeFromCache(ICollectionPersister persister, object
196196

197197
array = System.Array.CreateInstance(persister.ElementClass, cached.Length);
198198

199+
var elementType = persister.ElementType;
199200
for (int i = 0; i < cached.Length; i++)
200201
{
201-
array.SetValue(persister.ElementType.Assemble(cached[i], Session, owner), i);
202+
elementType.BeforeAssemble(cached[i], Session);
203+
}
204+
205+
for (int i = 0; i < cached.Length; i++)
206+
{
207+
array.SetValue(elementType.Assemble(cached[i], Session, owner), i);
202208
}
203209
}
204210

0 commit comments

Comments
 (0)