@@ -155,14 +155,15 @@ protected virtual IQueryable<TResource> GetAll()
155
155
}
156
156
157
157
/// <inheritdoc />
158
- public virtual Task < TResource > GetForCreateAsync ( TId id , CancellationToken cancellationToken )
158
+ public virtual Task < TResource > GetForCreateAsync ( Type resourceClrType , TId id , CancellationToken cancellationToken )
159
159
{
160
160
_traceWriter . LogMethodStart ( new
161
161
{
162
+ resourceClrType ,
162
163
id
163
164
} ) ;
164
165
165
- var resource = _resourceFactory . CreateInstance < TResource > ( ) ;
166
+ var resource = ( TResource ) _resourceFactory . CreateInstance ( resourceClrType ) ;
166
167
resource . Id = id ;
167
168
168
169
return Task . FromResult ( resource ) ;
@@ -305,18 +306,19 @@ protected void AssertIsNotClearingRequiredToOneRelationship(RelationshipAttribut
305
306
}
306
307
307
308
/// <inheritdoc />
308
- public virtual async Task DeleteAsync ( TId id , CancellationToken cancellationToken )
309
+ public virtual async Task DeleteAsync ( TResource ? resourceFromDatabase , TId id , CancellationToken cancellationToken )
309
310
{
310
311
_traceWriter . LogMethodStart ( new
311
312
{
313
+ resourceFromDatabase ,
312
314
id
313
315
} ) ;
314
316
315
317
using IDisposable _ = CodeTimingSessionManager . Current . Measure ( "Repository - Delete resource" ) ;
316
318
317
319
// This enables OnWritingAsync() to fetch the resource, which adds it to the change tracker.
318
320
// If so, we'll reuse the tracked resource instead of this placeholder resource.
319
- var placeholderResource = _resourceFactory . CreateInstance < TResource > ( ) ;
321
+ TResource placeholderResource = resourceFromDatabase ?? _resourceFactory . CreateInstance < TResource > ( ) ;
320
322
placeholderResource . Id = id ;
321
323
322
324
await _resourceDefinitionAccessor . OnWritingAsync ( placeholderResource , WriteOperationKind . DeleteResource , cancellationToken ) ;
@@ -413,10 +415,12 @@ public virtual async Task SetRelationshipAsync(TResource leftResource, object? r
413
415
}
414
416
415
417
/// <inheritdoc />
416
- public virtual async Task AddToToManyRelationshipAsync ( TId leftId , ISet < IIdentifiable > rightResourceIds , CancellationToken cancellationToken )
418
+ public virtual async Task AddToToManyRelationshipAsync ( TResource ? leftResource , TId leftId , ISet < IIdentifiable > rightResourceIds ,
419
+ CancellationToken cancellationToken )
417
420
{
418
421
_traceWriter . LogMethodStart ( new
419
422
{
423
+ leftResource ,
420
424
leftId ,
421
425
rightResourceIds
422
426
} ) ;
@@ -427,25 +431,53 @@ public virtual async Task AddToToManyRelationshipAsync(TId leftId, ISet<IIdentif
427
431
428
432
var relationship = ( HasManyAttribute ) _targetedFields . Relationships . Single ( ) ;
429
433
430
- await _resourceDefinitionAccessor . OnAddToRelationshipAsync < TResource , TId > ( leftId , relationship , rightResourceIds , cancellationToken ) ;
434
+ // This enables OnAddToRelationshipAsync() or OnWritingAsync() to fetch the resource, which adds it to the change tracker.
435
+ // If so, we'll reuse the tracked resource instead of this placeholder resource.
436
+ TResource leftPlaceholderResource = leftResource ?? _resourceFactory . CreateInstance < TResource > ( ) ;
437
+ leftPlaceholderResource . Id = leftId ;
438
+
439
+ await _resourceDefinitionAccessor . OnAddToRelationshipAsync ( leftPlaceholderResource , relationship , rightResourceIds , cancellationToken ) ;
431
440
432
441
if ( rightResourceIds . Any ( ) )
433
442
{
434
- var leftPlaceholderResource = _resourceFactory . CreateInstance < TResource > ( ) ;
435
- leftPlaceholderResource . Id = leftId ;
436
-
437
443
var leftResourceTracked = ( TResource ) _dbContext . GetTrackedOrAttach ( leftPlaceholderResource ) ;
444
+ IEnumerable rightValueToStore = GetRightValueToStoreForAddToToMany ( leftResourceTracked , relationship , rightResourceIds ) ;
438
445
439
- await UpdateRelationshipAsync ( relationship , leftResourceTracked , rightResourceIds , cancellationToken ) ;
446
+ await UpdateRelationshipAsync ( relationship , leftResourceTracked , rightValueToStore , cancellationToken ) ;
440
447
441
448
await _resourceDefinitionAccessor . OnWritingAsync ( leftResourceTracked , WriteOperationKind . AddToRelationship , cancellationToken ) ;
449
+ leftResourceTracked = ( TResource ) _dbContext . GetTrackedOrAttach ( leftResourceTracked ) ;
442
450
443
451
await SaveChangesAsync ( cancellationToken ) ;
444
452
445
453
await _resourceDefinitionAccessor . OnWriteSucceededAsync ( leftResourceTracked , WriteOperationKind . AddToRelationship , cancellationToken ) ;
446
454
}
447
455
}
448
456
457
+ private IEnumerable GetRightValueToStoreForAddToToMany ( TResource leftResource , HasManyAttribute relationship , ISet < IIdentifiable > rightResourceIdsToAdd )
458
+ {
459
+ object ? rightValueStored = relationship . GetValue ( leftResource ) ;
460
+
461
+ // @formatter:wrap_chained_method_calls chop_always
462
+ // @formatter:keep_existing_linebreaks true
463
+
464
+ HashSet < IIdentifiable > rightResourceIdsStored = _collectionConverter
465
+ . ExtractResources ( rightValueStored )
466
+ . Select ( rightResource => _dbContext . GetTrackedOrAttach ( rightResource ) )
467
+ . ToHashSet ( IdentifiableComparer . Instance ) ;
468
+
469
+ // @formatter:keep_existing_linebreaks restore
470
+ // @formatter:wrap_chained_method_calls restore
471
+
472
+ if ( rightResourceIdsStored . Any ( ) )
473
+ {
474
+ rightResourceIdsStored . AddRange ( rightResourceIdsToAdd ) ;
475
+ return rightResourceIdsStored ;
476
+ }
477
+
478
+ return rightResourceIdsToAdd ;
479
+ }
480
+
449
481
/// <inheritdoc />
450
482
public virtual async Task RemoveFromToManyRelationshipAsync ( TResource leftResource , ISet < IIdentifiable > rightResourceIds ,
451
483
CancellationToken cancellationToken )
@@ -473,7 +505,7 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TResource leftResour
473
505
// Make Entity Framework Core believe any additional resources added from ResourceDefinition already exist in database.
474
506
IIdentifiable [ ] extraResourceIdsToRemove = rightResourceIdsToRemove . Where ( rightId => ! rightResourceIds . Contains ( rightId ) ) . ToArray ( ) ;
475
507
476
- object ? rightValueStored = relationship . GetValue ( leftResource ) ;
508
+ object ? rightValueStored = relationship . GetValue ( leftResourceTracked ) ;
477
509
478
510
// @formatter:wrap_chained_method_calls chop_always
479
511
// @formatter:keep_existing_linebreaks true
@@ -488,9 +520,9 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TResource leftResour
488
520
// @formatter:wrap_chained_method_calls restore
489
521
490
522
rightValueStored = _collectionConverter . CopyToTypedCollection ( rightResourceIdsStored , relationship . Property . PropertyType ) ;
491
- relationship . SetValue ( leftResource , rightValueStored ) ;
523
+ relationship . SetValue ( leftResourceTracked , rightValueStored ) ;
492
524
493
- MarkRelationshipAsLoaded ( leftResource , relationship ) ;
525
+ MarkRelationshipAsLoaded ( leftResourceTracked , relationship ) ;
494
526
495
527
HashSet < IIdentifiable > rightResourceIdsToStore = rightResourceIdsStored . ToHashSet ( IdentifiableComparer . Instance ) ;
496
528
rightResourceIdsToStore . ExceptWith ( rightResourceIdsToRemove ) ;
0 commit comments