Skip to content

Commit 1e8fe9c

Browse files
fixup! Fix extra-lazy collection forgetting changes
Handle NH-739 fix
1 parent 16911ff commit 1e8fe9c

File tree

1 file changed

+23
-50
lines changed

1 file changed

+23
-50
lines changed

src/NHibernate/Collection/Generic/PersistentGenericBag.cs

Lines changed: 23 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ public partial class PersistentGenericBag<T> : AbstractPersistentCollection, ILi
4848
* <one-to-many> <bag>!
4949
*/
5050
private IList<T> _gbag;
51+
private bool _isOneToMany;
5152

5253
public PersistentGenericBag()
5354
{
@@ -255,59 +256,10 @@ public void RemoveAt(int index)
255256
_gbag.RemoveAt(index);
256257
}
257258

258-
public override bool AfterInitialize(ICollectionPersister persister)
259-
{
260-
// NH Different behavior : NH-739
261-
// would be nice to prevent this overhead but the operation is managed where the ICollectionPersister is not available
262-
bool result;
263-
if (persister.IsOneToMany && HasQueuedOperations)
264-
{
265-
var additionStartFrom = _gbag.Count;
266-
IList additionQueue = new List<object>(additionStartFrom);
267-
foreach (var o in QueuedAdditionIterator)
268-
{
269-
if (o != null)
270-
{
271-
for (var i = 0; i < _gbag.Count; i++)
272-
{
273-
// we are using ReferenceEquals to be sure that is exactly the same queued instance
274-
if (ReferenceEquals(o, _gbag[i]))
275-
{
276-
additionQueue.Add(o);
277-
break;
278-
}
279-
}
280-
}
281-
}
282-
283-
result = base.AfterInitialize(persister);
284-
285-
if (!result)
286-
{
287-
// removing duplicated additions
288-
foreach (var o in additionQueue)
289-
{
290-
for (var i = additionStartFrom; i < _gbag.Count; i++)
291-
{
292-
if (ReferenceEquals(o, _gbag[i]))
293-
{
294-
_gbag.RemoveAt(i);
295-
break;
296-
}
297-
}
298-
}
299-
}
300-
}
301-
else
302-
{
303-
result = base.AfterInitialize(persister);
304-
}
305-
return result;
306-
}
307-
308259
public override void BeforeInitialize(ICollectionPersister persister, int anticipatedSize)
309260
{
310261
_gbag = (IList<T>) persister.CollectionType.Instantiate(anticipatedSize);
262+
_isOneToMany = persister.IsOneToMany;
311263
}
312264

313265
public override object Disassemble(ICollectionPersister persister)
@@ -597,6 +549,27 @@ public object Orphan
597549

598550
public void Operate()
599551
{
552+
// NH Different behavior for NH-739. A "bag" mapped as a bidirectional one-to-many of an entity with an
553+
// id generator causing it to be inserted on flush must not replay the addition after initialization,
554+
// if the entity was previously saved. In that case, the entity save has inserted it in database with
555+
// its association to the bag, without causing a full flush. So for the bag, the operation is still
556+
// pending, but in database it is already done. On initialization, the bag thus already receives the
557+
// entity in its loaded list, and it should not be added again.
558+
// Since a one-to-many bag is actually a set, we can simply check if the entity is already in the loaded
559+
// state, and discard it if yes. (It also relies on the bag not having pending removes, which is the
560+
// case, since it only handles delayed additions and clears.)
561+
// Since this condition happens with transient instances added in the bag then saved, ReferenceEquals
562+
// is enough to match them.
563+
// This solution is a workaround, the root cause is not fixed. The root cause is the insertion on save
564+
// done without caring for pending operations of one-to-many collections. This root cause could be fixed
565+
// by triggering a full flush there before the insert (currently it just flushes pending inserts), or
566+
// maybe by flushing just the dirty one-to-many non-initialized collections (but this can be tricky).
567+
// (It is also likely one-to-many lists have a similar issue, but nothing is done yet for them. And
568+
// their case is more complex due to having to care for the indexes and to handle many more delayed
569+
// operation kinds.)
570+
if (_enclosingInstance._isOneToMany && _enclosingInstance._gbag.Any(l => ReferenceEquals(l, _value)))
571+
return;
572+
600573
_enclosingInstance._gbag.Add(_value);
601574
}
602575
}

0 commit comments

Comments
 (0)