@@ -172,20 +172,48 @@ public virtual async Task CreateAsync(TResource resourceFromRequest, TResource r
172
172
foreach ( RelationshipAttribute relationship in _targetedFields . Relationships )
173
173
{
174
174
object rightResources = relationship . GetValue ( resourceFromRequest ) ;
175
- await UpdateRelationshipAsync ( relationship , resourceForDatabase , rightResources , collector , cancellationToken ) ;
175
+
176
+ object rightResourcesEdited = await EditRelationshipAsync ( resourceForDatabase , relationship , rightResources , OperationKind . CreateResource ,
177
+ cancellationToken ) ;
178
+
179
+ await UpdateRelationshipAsync ( relationship , resourceForDatabase , rightResourcesEdited , collector , cancellationToken ) ;
176
180
}
177
181
178
182
foreach ( AttrAttribute attribute in _targetedFields . Attributes )
179
183
{
180
184
attribute . SetValue ( resourceForDatabase , attribute . GetValue ( resourceFromRequest ) ) ;
181
185
}
182
186
183
- await _resourceDefinitionAccessor . OnBeforeCreateResourceAsync ( resourceForDatabase , cancellationToken ) ;
187
+ await _resourceDefinitionAccessor . OnWritingAsync ( resourceForDatabase , OperationKind . CreateResource , cancellationToken ) ;
184
188
185
189
DbSet < TResource > dbSet = _dbContext . Set < TResource > ( ) ;
186
190
await dbSet . AddAsync ( resourceForDatabase , cancellationToken ) ;
187
191
188
192
await SaveChangesAsync ( cancellationToken ) ;
193
+
194
+ await _resourceDefinitionAccessor . OnWriteSucceededAsync ( resourceForDatabase , OperationKind . CreateResource , cancellationToken ) ;
195
+ }
196
+
197
+ private async Task < object > EditRelationshipAsync ( TResource leftResource , RelationshipAttribute relationship , object rightResourceIds ,
198
+ OperationKind operationKind , CancellationToken cancellationToken )
199
+ {
200
+ if ( relationship is HasOneAttribute hasOneRelationship )
201
+ {
202
+ return await _resourceDefinitionAccessor . OnSetToOneRelationshipAsync ( leftResource , hasOneRelationship , ( IIdentifiable ) rightResourceIds ,
203
+ operationKind , cancellationToken ) ;
204
+ }
205
+
206
+ if ( relationship is HasManyAttribute hasManyRelationship )
207
+ {
208
+ HashSet < IIdentifiable > rightResourceIdSet = _collectionConverter . ExtractResources ( rightResourceIds ) . ToHashSet ( IdentifiableComparer . Instance ) ;
209
+
210
+ await _resourceDefinitionAccessor . OnSetToManyRelationshipAsync ( leftResource , hasManyRelationship , rightResourceIdSet , operationKind ,
211
+ cancellationToken ) ;
212
+
213
+ return rightResourceIdSet ;
214
+ }
215
+
216
+ return rightResourceIds ;
189
217
}
190
218
191
219
/// <inheritdoc />
@@ -213,19 +241,24 @@ public virtual async Task UpdateAsync(TResource resourceFromRequest, TResource r
213
241
{
214
242
object rightResources = relationship . GetValue ( resourceFromRequest ) ;
215
243
216
- AssertIsNotClearingRequiredRelationship ( relationship , resourceFromDatabase , rightResources ) ;
244
+ object rightResourcesEdited = await EditRelationshipAsync ( resourceFromDatabase , relationship , rightResources , OperationKind . UpdateResource ,
245
+ cancellationToken ) ;
217
246
218
- await UpdateRelationshipAsync ( relationship , resourceFromDatabase , rightResources , collector , cancellationToken ) ;
247
+ AssertIsNotClearingRequiredRelationship ( relationship , resourceFromDatabase , rightResourcesEdited ) ;
248
+
249
+ await UpdateRelationshipAsync ( relationship , resourceFromDatabase , rightResourcesEdited , collector , cancellationToken ) ;
219
250
}
220
251
221
252
foreach ( AttrAttribute attribute in _targetedFields . Attributes )
222
253
{
223
254
attribute . SetValue ( resourceFromDatabase , attribute . GetValue ( resourceFromRequest ) ) ;
224
255
}
225
256
226
- await _resourceDefinitionAccessor . OnBeforeUpdateResourceAsync ( resourceFromDatabase , cancellationToken ) ;
257
+ await _resourceDefinitionAccessor . OnWritingAsync ( resourceFromDatabase , OperationKind . UpdateResource , cancellationToken ) ;
227
258
228
259
await SaveChangesAsync ( cancellationToken ) ;
260
+
261
+ await _resourceDefinitionAccessor . OnWriteSucceededAsync ( resourceFromDatabase , OperationKind . UpdateResource , cancellationToken ) ;
229
262
}
230
263
231
264
protected void AssertIsNotClearingRequiredRelationship ( RelationshipAttribute relationship , TResource leftResource , object rightValue )
@@ -238,9 +271,9 @@ protected void AssertIsNotClearingRequiredRelationship(RelationshipAttribute rel
238
271
relationshipIsRequired = navigation ? . ForeignKey ? . IsRequired ?? false ;
239
272
}
240
273
241
- bool relationshipIsBeingCleared = relationship is HasOneAttribute
242
- ? rightValue == null
243
- : IsToManyRelationshipBeingCleared ( relationship , leftResource , rightValue ) ;
274
+ bool relationshipIsBeingCleared = relationship is HasManyAttribute hasManyRelationship
275
+ ? IsToManyRelationshipBeingCleared ( hasManyRelationship , leftResource , rightValue )
276
+ : rightValue == null ;
244
277
245
278
if ( relationshipIsRequired && relationshipIsBeingCleared )
246
279
{
@@ -249,11 +282,11 @@ protected void AssertIsNotClearingRequiredRelationship(RelationshipAttribute rel
249
282
}
250
283
}
251
284
252
- private bool IsToManyRelationshipBeingCleared ( RelationshipAttribute relationship , TResource leftResource , object valueToAssign )
285
+ private bool IsToManyRelationshipBeingCleared ( HasManyAttribute hasManyRelationship , TResource leftResource , object valueToAssign )
253
286
{
254
287
ICollection < IIdentifiable > newRightResourceIds = _collectionConverter . ExtractResources ( valueToAssign ) ;
255
288
256
- object existingRightValue = relationship . GetValue ( leftResource ) ;
289
+ object existingRightValue = hasManyRelationship . GetValue ( leftResource ) ;
257
290
258
291
HashSet < IIdentifiable > existingRightResourceIds =
259
292
_collectionConverter . ExtractResources ( existingRightValue ) . ToHashSet ( IdentifiableComparer . Instance ) ;
@@ -271,6 +304,13 @@ public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToke
271
304
id
272
305
} ) ;
273
306
307
+ // This enables OnWritingAsync() to fetch the resource, which adds it to the change tracker.
308
+ // If so, we'll reuse the tracked resource instead of a placeholder resource.
309
+ var emptyResource = _resourceFactory . CreateInstance < TResource > ( ) ;
310
+ emptyResource . Id = id ;
311
+
312
+ await _resourceDefinitionAccessor . OnWritingAsync ( emptyResource , OperationKind . DeleteResource , cancellationToken ) ;
313
+
274
314
using var collector = new PlaceholderResourceCollector ( _resourceFactory , _dbContext ) ;
275
315
TResource resource = collector . CreateForId < TResource , TId > ( id ) ;
276
316
@@ -288,6 +328,8 @@ public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToke
288
328
_dbContext . Remove ( resource ) ;
289
329
290
330
await SaveChangesAsync ( cancellationToken ) ;
331
+
332
+ await _resourceDefinitionAccessor . OnWriteSucceededAsync ( resource , OperationKind . DeleteResource , cancellationToken ) ;
291
333
}
292
334
293
335
private NavigationEntry GetNavigationEntry ( TResource resource , RelationshipAttribute relationship )
@@ -349,12 +391,19 @@ public virtual async Task SetRelationshipAsync(TResource primaryResource, object
349
391
350
392
RelationshipAttribute relationship = _targetedFields . Relationships . Single ( ) ;
351
393
352
- AssertIsNotClearingRequiredRelationship ( relationship , primaryResource , secondaryResourceIds ) ;
394
+ object secondaryResourceIdsEdited =
395
+ await EditRelationshipAsync ( primaryResource , relationship , secondaryResourceIds , OperationKind . SetRelationship , cancellationToken ) ;
396
+
397
+ AssertIsNotClearingRequiredRelationship ( relationship , primaryResource , secondaryResourceIdsEdited ) ;
353
398
354
399
using var collector = new PlaceholderResourceCollector ( _resourceFactory , _dbContext ) ;
355
- await UpdateRelationshipAsync ( relationship , primaryResource , secondaryResourceIds , collector , cancellationToken ) ;
400
+ await UpdateRelationshipAsync ( relationship , primaryResource , secondaryResourceIdsEdited , collector , cancellationToken ) ;
401
+
402
+ await _resourceDefinitionAccessor . OnWritingAsync ( primaryResource , OperationKind . SetRelationship , cancellationToken ) ;
356
403
357
404
await SaveChangesAsync ( cancellationToken ) ;
405
+
406
+ await _resourceDefinitionAccessor . OnWriteSucceededAsync ( primaryResource , OperationKind . SetRelationship , cancellationToken ) ;
358
407
}
359
408
360
409
/// <inheritdoc />
@@ -368,7 +417,9 @@ public virtual async Task AddToToManyRelationshipAsync(TId primaryId, ISet<IIden
368
417
369
418
ArgumentGuard . NotNull ( secondaryResourceIds , nameof ( secondaryResourceIds ) ) ;
370
419
371
- RelationshipAttribute relationship = _targetedFields . Relationships . Single ( ) ;
420
+ var relationship = ( HasManyAttribute ) _targetedFields . Relationships . Single ( ) ;
421
+
422
+ await _resourceDefinitionAccessor . OnAddToRelationshipAsync < TResource , TId > ( primaryId , relationship , secondaryResourceIds , cancellationToken ) ;
372
423
373
424
if ( secondaryResourceIds . Any ( ) )
374
425
{
@@ -377,7 +428,11 @@ public virtual async Task AddToToManyRelationshipAsync(TId primaryId, ISet<IIden
377
428
378
429
await UpdateRelationshipAsync ( relationship , primaryResource , secondaryResourceIds , collector , cancellationToken ) ;
379
430
431
+ await _resourceDefinitionAccessor . OnWritingAsync ( primaryResource , OperationKind . AddToRelationship , cancellationToken ) ;
432
+
380
433
await SaveChangesAsync ( cancellationToken ) ;
434
+
435
+ await _resourceDefinitionAccessor . OnWriteSucceededAsync ( primaryResource , OperationKind . AddToRelationship , cancellationToken ) ;
381
436
}
382
437
}
383
438
@@ -395,17 +450,26 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TResource primaryRes
395
450
396
451
var relationship = ( HasManyAttribute ) _targetedFields . Relationships . Single ( ) ;
397
452
398
- object rightValue = relationship . GetValue ( primaryResource ) ;
453
+ await _resourceDefinitionAccessor . OnRemoveFromRelationshipAsync ( primaryResource , relationship , secondaryResourceIds , cancellationToken ) ;
399
454
400
- HashSet < IIdentifiable > rightResourceIds = _collectionConverter . ExtractResources ( rightValue ) . ToHashSet ( IdentifiableComparer . Instance ) ;
401
- rightResourceIds . ExceptWith ( secondaryResourceIds ) ;
455
+ if ( secondaryResourceIds . Any ( ) )
456
+ {
457
+ object rightValue = relationship . GetValue ( primaryResource ) ;
402
458
403
- AssertIsNotClearingRequiredRelationship ( relationship , primaryResource , rightResourceIds ) ;
459
+ HashSet < IIdentifiable > rightResourceIds = _collectionConverter . ExtractResources ( rightValue ) . ToHashSet ( IdentifiableComparer . Instance ) ;
460
+ rightResourceIds . ExceptWith ( secondaryResourceIds ) ;
404
461
405
- using var collector = new PlaceholderResourceCollector ( _resourceFactory , _dbContext ) ;
406
- await UpdateRelationshipAsync ( relationship , primaryResource , rightResourceIds , collector , cancellationToken ) ;
462
+ AssertIsNotClearingRequiredRelationship ( relationship , primaryResource , rightResourceIds ) ;
407
463
408
- await SaveChangesAsync ( cancellationToken ) ;
464
+ using var collector = new PlaceholderResourceCollector ( _resourceFactory , _dbContext ) ;
465
+ await UpdateRelationshipAsync ( relationship , primaryResource , rightResourceIds , collector , cancellationToken ) ;
466
+
467
+ await _resourceDefinitionAccessor . OnWritingAsync ( primaryResource , OperationKind . RemoveFromRelationship , cancellationToken ) ;
468
+
469
+ await SaveChangesAsync ( cancellationToken ) ;
470
+
471
+ await _resourceDefinitionAccessor . OnWriteSucceededAsync ( primaryResource , OperationKind . RemoveFromRelationship , cancellationToken ) ;
472
+ }
409
473
}
410
474
411
475
protected async Task UpdateRelationshipAsync ( RelationshipAttribute relationship , TResource leftResource , object valueToAssign ,
@@ -458,6 +522,8 @@ private bool IsOneToOneRelationship(RelationshipAttribute relationship)
458
522
459
523
protected virtual async Task SaveChangesAsync ( CancellationToken cancellationToken )
460
524
{
525
+ cancellationToken . ThrowIfCancellationRequested ( ) ;
526
+
461
527
try
462
528
{
463
529
await _dbContext . SaveChangesAsync ( cancellationToken ) ;
0 commit comments