@@ -315,83 +315,8 @@ public void SortActions()
315
315
//violations
316
316
private void SortInsertActions ( )
317
317
{
318
- // IMPLEMENTATION NOTES:
319
- //
320
- // The main data structure in this ordering algorithm is the 'positionToAction'
321
- // map. Essentially this can be thought of as an put-ordered map (the problem with
322
- // actually implementing it that way and doing away with the 'nameList' is that
323
- // we'd end up having potential duplicate key values). 'positionToAction' maintains
324
- // a mapping from a position within the 'nameList' structure to a "partial queue"
325
- // of actions.
326
-
327
- Dictionary < int , List < EntityInsertAction > > positionToAction =
328
- new Dictionary < int , List < EntityInsertAction > > ( ) ;
329
- List < string > nameList = new List < string > ( ) ;
330
-
331
- while ( ! ( insertions . Count == 0 ) )
332
- {
333
- // todo-events : test behaviour
334
- // in Java they use an implicit cast to EntityInsertAction
335
- // but it may be not work because the insertions list may contain EntityIdentityInsertAction
336
- // (I don't like that "goto"too)
337
- object tempObject = insertions [ 0 ] ;
338
- insertions . RemoveAt ( 0 ) ;
339
- EntityInsertAction action = ( EntityInsertAction ) tempObject ;
340
- string thisEntityName = action . EntityName ;
341
-
342
- // see if we have already encountered this entity-name...
343
- if ( ! nameList . Contains ( thisEntityName ) )
344
- {
345
- // we have not, so create the proper entries in nameList and positionToAction
346
- List < EntityInsertAction > segmentedActionQueue = new List < EntityInsertAction > ( ) ;
347
- segmentedActionQueue . Add ( action ) ;
348
- nameList . Add ( thisEntityName ) ;
349
- positionToAction [ nameList . IndexOf ( thisEntityName ) ] = segmentedActionQueue ;
318
+ new InsertActionSorter ( this ) . Sort ( ) ;
350
319
}
351
- else
352
- {
353
- // we have seen it before, so we need to determine if this insert action is
354
- // is dependent upon a previously processed action in terms of FK
355
- // relationships (this FK checking is done against the entity's property-state
356
- // associated with the action...)
357
- int lastPos = nameList . LastIndexOf ( thisEntityName ) ;
358
- object [ ] states = action . State ;
359
- for ( int i = 0 ; i < states . Length ; i ++ )
360
- {
361
- for ( int j = 0 ; j < nameList . Count ; j ++ )
362
- {
363
- List < EntityInsertAction > tmpList = positionToAction [ j ] ;
364
- for ( int k = 0 ; k < tmpList . Count ; k ++ )
365
- {
366
- EntityInsertAction checkAction = tmpList [ k ] ;
367
- if ( checkAction . Instance == states [ i ] && j > lastPos )
368
- {
369
- // 'checkAction' is inserting an entity upon which 'action' depends...
370
- // note: this is an assumption and may not be correct in the case of one-to-one
371
- List < EntityInsertAction > segmentedActionQueue = new List < EntityInsertAction > ( ) ;
372
- segmentedActionQueue . Add ( action ) ;
373
- nameList . Add ( thisEntityName ) ;
374
- positionToAction [ nameList . LastIndexOf ( thisEntityName ) ] = segmentedActionQueue ;
375
- goto loopInsertion ;
376
- }
377
- }
378
- }
379
- }
380
-
381
- List < EntityInsertAction > actionQueue = positionToAction [ lastPos ] ;
382
- actionQueue . Add ( action ) ;
383
- }
384
- loopInsertion : ;
385
- }
386
-
387
- // now iterate back through positionToAction map and move entityInsertAction back to insertion list
388
- for ( int p = 0 ; p < nameList . Count ; p ++ )
389
- {
390
- List < EntityInsertAction > actionQueue = positionToAction [ p ] ;
391
- foreach ( EntityInsertAction action in actionQueue )
392
- insertions . Add ( action ) ;
393
- }
394
- }
395
320
396
321
public IList < EntityDeleteAction > CloneDeletions ( )
397
322
{
@@ -575,5 +500,129 @@ public void AfterTransactionCompletion(bool success)
575
500
querySpacesToInvalidate . Clear ( ) ;
576
501
}
577
502
}
503
+
504
+ [ Serializable ]
505
+ private class InsertActionSorter
506
+ {
507
+ private readonly ActionQueue _actionQueue ;
508
+
509
+ // the mapping of entity names to their latest batch numbers.
510
+ private readonly Dictionary < string , int > _latestBatches = new Dictionary < string , int > ( ) ;
511
+ private readonly Dictionary < object , int > _entityBatchNumber ;
512
+
513
+ // the map of batch numbers to EntityInsertAction lists
514
+ private readonly Dictionary < int , List < EntityInsertAction > > _actionBatches = new Dictionary < int , List < EntityInsertAction > > ( ) ;
515
+
516
+ public InsertActionSorter ( ActionQueue actionQueue )
517
+ {
518
+ _actionQueue = actionQueue ;
519
+
520
+ //optimize the hash size to eliminate a rehash.
521
+ _entityBatchNumber = new Dictionary < object , int > ( actionQueue . insertions . Count + 1 ) ;
522
+ }
523
+
524
+ public void Sort ( )
525
+ {
526
+ // the list of entity names that indicate the batch number
527
+ foreach ( EntityInsertAction action in _actionQueue . insertions )
528
+ {
529
+ // remove the current element from insertions. It will be added back later.
530
+ var entityName = action . EntityName ;
531
+
532
+ // the entity associated with the current action.
533
+ var currentEntity = action . Instance ;
534
+
535
+ int batchNumber ;
536
+ if ( _latestBatches . ContainsKey ( entityName ) )
537
+ {
538
+ // There is already an existing batch for this type of entity.
539
+ // Check to see if the latest batch is acceptable.
540
+ batchNumber = FindBatchNumber ( action , entityName ) ;
541
+ }
542
+ else
543
+ {
544
+ // add an entry for this type of entity.
545
+ // we can be assured that all referenced entities have already
546
+ // been processed,
547
+ // so specify that this entity is with the latest batch.
548
+ // doing the batch number before adding the name to the list is
549
+ // a faster way to get an accurate number.
550
+
551
+ batchNumber = _actionBatches . Count ;
552
+ _latestBatches [ entityName ] = batchNumber ;
553
+ }
554
+ _entityBatchNumber [ currentEntity ] = batchNumber ;
555
+ AddToBatch ( batchNumber , action ) ;
556
+ }
557
+ _actionQueue . insertions . Clear ( ) ;
558
+
559
+ // now rebuild the insertions list. There is a batch for each entry in the name list.
560
+ for ( var i = 0 ; i < _actionBatches . Count ; i ++ )
561
+ {
562
+ var batch = _actionBatches [ i ] ;
563
+ foreach ( var action in batch )
564
+ {
565
+ _actionQueue . insertions . Add ( action ) ;
566
+ }
567
+ }
568
+ }
569
+
570
+ /// <summary>
571
+ /// Finds an acceptable batch for this entity to be a member as part of the <see cref="InsertActionSorter" />
572
+ /// </summary>
573
+ /// <param name="action">The action being sorted</param>
574
+ /// <param name="entityName">The name of the entity affected by the action</param>
575
+ /// <returns>An appropriate batch number; todo document this process better</returns>
576
+ private int FindBatchNumber ( EntityInsertAction action , string entityName )
577
+ {
578
+ // loop through all the associated entities and make sure they have been
579
+ // processed before the latest
580
+ // batch associated with this entity type.
581
+
582
+ // the current batch number is the latest batch for this entity type.
583
+ var latestBatchNumberForType = _latestBatches [ entityName ] ;
584
+
585
+ // loop through all the associations of the current entity and make sure that they are processed
586
+ // before the current batch number
587
+ var propertyValues = action . State ;
588
+ var propertyTypes = action . Persister . ClassMetadata . PropertyTypes ;
589
+
590
+ for ( var i = 0 ; i < propertyValues . Length ; i ++ )
591
+ {
592
+ var value = propertyValues [ i ] ;
593
+ var type = propertyTypes [ i ] ;
594
+
595
+ if ( type . IsEntityType && value != null )
596
+ {
597
+ // find the batch number associated with the current association, if any.
598
+ int associationBatchNumber ;
599
+ if ( _entityBatchNumber . TryGetValue ( value , out associationBatchNumber ) && associationBatchNumber > latestBatchNumberForType )
600
+ {
601
+ // create a new batch for this type. The batch number is the number of current batches.
602
+ latestBatchNumberForType = _actionBatches . Count ;
603
+ _latestBatches [ entityName ] = latestBatchNumberForType ;
604
+ // since this entity will now be processed in the latest possible batch,
605
+ // we can be assured that it will come after all other associations,
606
+ // there's not need to continue checking.
607
+ break ;
608
+ }
609
+ }
610
+ }
611
+ return latestBatchNumberForType ;
612
+ }
613
+
614
+ private void AddToBatch ( int batchNumber , EntityInsertAction action )
615
+ {
616
+ List < EntityInsertAction > actions ;
617
+
618
+ if ( ! _actionBatches . TryGetValue ( batchNumber , out actions ) )
619
+ {
620
+ actions = new List < EntityInsertAction > ( ) ;
621
+ _actionBatches [ batchNumber ] = actions ;
622
+ }
623
+
624
+ actions . Add ( action ) ;
625
+ }
626
+ }
578
627
}
579
628
}
0 commit comments