@@ -48,6 +48,7 @@ public partial class PersistentGenericBag<T> : AbstractPersistentCollection, ILi
48
48
* <one-to-many> <bag>!
49
49
*/
50
50
private IList < T > _gbag ;
51
+ private bool _isOneToMany ;
51
52
52
53
public PersistentGenericBag ( )
53
54
{
@@ -255,59 +256,10 @@ public void RemoveAt(int index)
255
256
_gbag . RemoveAt ( index ) ;
256
257
}
257
258
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
-
308
259
public override void BeforeInitialize ( ICollectionPersister persister , int anticipatedSize )
309
260
{
310
261
_gbag = ( IList < T > ) persister . CollectionType . Instantiate ( anticipatedSize ) ;
262
+ _isOneToMany = persister . IsOneToMany ;
311
263
}
312
264
313
265
public override object Disassemble ( ICollectionPersister persister )
@@ -597,6 +549,27 @@ public object Orphan
597
549
598
550
public void Operate ( )
599
551
{
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
+
600
573
_enclosingInstance . _gbag . Add ( _value ) ;
601
574
}
602
575
}
0 commit comments