@@ -26,6 +26,7 @@ public class EntityFrameworkCoreRepository<TResource, TId> : IResourceRepository
26
26
where TResource : class , IIdentifiable < TId >
27
27
{
28
28
private readonly CollectionConverter _collectionConverter = new ( ) ;
29
+ private readonly IJsonApiRequest _request ;
29
30
private readonly ITargetedFields _targetedFields ;
30
31
private readonly DbContext _dbContext ;
31
32
private readonly IResourceGraph _resourceGraph ;
@@ -37,24 +38,26 @@ public class EntityFrameworkCoreRepository<TResource, TId> : IResourceRepository
37
38
/// <inheritdoc />
38
39
public virtual string ? TransactionId => _dbContext . Database . CurrentTransaction ? . TransactionId . ToString ( ) ;
39
40
40
- public EntityFrameworkCoreRepository ( ITargetedFields targetedFields , IDbContextResolver dbContextResolver , IResourceGraph resourceGraph ,
41
- IResourceFactory resourceFactory , IEnumerable < IQueryConstraintProvider > constraintProviders , ILoggerFactory loggerFactory ,
42
- IResourceDefinitionAccessor resourceDefinitionAccessor )
41
+ public EntityFrameworkCoreRepository ( IJsonApiRequest request , ITargetedFields targetedFields , IDbContextResolver dbContextResolver ,
42
+ IResourceGraph resourceGraph , IResourceFactory resourceFactory , IResourceDefinitionAccessor resourceDefinitionAccessor ,
43
+ IEnumerable < IQueryConstraintProvider > constraintProviders , ILoggerFactory loggerFactory )
43
44
{
45
+ ArgumentGuard . NotNull ( request , nameof ( request ) ) ;
44
46
ArgumentGuard . NotNull ( targetedFields , nameof ( targetedFields ) ) ;
45
47
ArgumentGuard . NotNull ( dbContextResolver , nameof ( dbContextResolver ) ) ;
46
48
ArgumentGuard . NotNull ( resourceGraph , nameof ( resourceGraph ) ) ;
47
49
ArgumentGuard . NotNull ( resourceFactory , nameof ( resourceFactory ) ) ;
50
+ ArgumentGuard . NotNull ( resourceDefinitionAccessor , nameof ( resourceDefinitionAccessor ) ) ;
48
51
ArgumentGuard . NotNull ( constraintProviders , nameof ( constraintProviders ) ) ;
49
52
ArgumentGuard . NotNull ( loggerFactory , nameof ( loggerFactory ) ) ;
50
- ArgumentGuard . NotNull ( resourceDefinitionAccessor , nameof ( resourceDefinitionAccessor ) ) ;
51
53
54
+ _request = request ;
52
55
_targetedFields = targetedFields ;
53
56
_dbContext = dbContextResolver . GetContext ( ) ;
54
57
_resourceGraph = resourceGraph ;
55
58
_resourceFactory = resourceFactory ;
56
- _constraintProviders = constraintProviders ;
57
59
_resourceDefinitionAccessor = resourceDefinitionAccessor ;
60
+ _constraintProviders = constraintProviders ;
58
61
_traceWriter = new TraceLogWriter < EntityFrameworkCoreRepository < TResource , TId > > ( loggerFactory ) ;
59
62
}
60
63
@@ -249,7 +252,11 @@ await _resourceDefinitionAccessor.OnSetToManyRelationshipAsync(leftResource, has
249
252
using IDisposable _ = CodeTimingSessionManager . Current . Measure ( "Repository - Get resource for update" ) ;
250
253
251
254
IReadOnlyCollection < TResource > resources = await GetAsync ( queryLayer , cancellationToken ) ;
252
- return resources . FirstOrDefault ( ) ;
255
+ TResource ? resource = resources . FirstOrDefault ( ) ;
256
+
257
+ resource ? . RestoreConcurrencyToken ( _dbContext , _request . PrimaryVersion ) ;
258
+
259
+ return resource ;
253
260
}
254
261
255
262
/// <inheritdoc />
@@ -324,6 +331,7 @@ public virtual async Task DeleteAsync(TResource? resourceFromDatabase, TId id, C
324
331
// If so, we'll reuse the tracked resource instead of this placeholder resource.
325
332
TResource placeholderResource = resourceFromDatabase ?? _resourceFactory . CreateInstance < TResource > ( ) ;
326
333
placeholderResource . Id = id ;
334
+ placeholderResource . RestoreConcurrencyToken ( _dbContext , _request . PrimaryVersion ) ;
327
335
328
336
await _resourceDefinitionAccessor . OnWritingAsync ( placeholderResource , WriteOperationKind . DeleteResource , cancellationToken ) ;
329
337
@@ -533,6 +541,17 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TResource leftResour
533
541
534
542
if ( ! rightResourceIdsToStore . SetEquals ( rightResourceIdsStored ) )
535
543
{
544
+ if ( relationship . RightType . IsVersioned )
545
+ {
546
+ foreach ( IIdentifiable rightResource in rightResourceIdsStored )
547
+ {
548
+ string ? requestVersion = rightResourceIdsToRemove . Single ( resource => resource . StringId == rightResource . StringId ) . GetVersion ( ) ;
549
+
550
+ rightResource . RestoreConcurrencyToken ( _dbContext , requestVersion ) ;
551
+ rightResource . RefreshConcurrencyValue ( ) ;
552
+ }
553
+ }
554
+
536
555
AssertIsNotClearingRequiredToOneRelationship ( relationship , leftResourceTracked , rightResourceIdsToStore ) ;
537
556
538
557
await UpdateRelationshipAsync ( relationship , leftResourceTracked , rightResourceIdsToStore , cancellationToken ) ;
@@ -566,6 +585,9 @@ protected async Task UpdateRelationshipAsync(RelationshipAttribute relationship,
566
585
await entityEntry . Reference ( inversePropertyName ) . LoadAsync ( cancellationToken ) ;
567
586
}
568
587
588
+ leftResource . RestoreConcurrencyToken ( _dbContext , _request . PrimaryVersion ) ;
589
+ leftResource . RefreshConcurrencyValue ( ) ;
590
+
569
591
relationship . SetValue ( leftResource , trackedValueToAssign ) ;
570
592
}
571
593
@@ -579,6 +601,13 @@ protected async Task UpdateRelationshipAsync(RelationshipAttribute relationship,
579
601
IReadOnlyCollection < IIdentifiable > rightResources = _collectionConverter . ExtractResources ( rightValue ) ;
580
602
IIdentifiable [ ] rightResourcesTracked = rightResources . Select ( rightResource => _dbContext . GetTrackedOrAttach ( rightResource ) ) . ToArray ( ) ;
581
603
604
+ foreach ( IIdentifiable rightResourceTracked in rightResourcesTracked )
605
+ {
606
+ string ? rightVersion = rightResourceTracked . GetVersion ( ) ;
607
+ rightResourceTracked . RestoreConcurrencyToken ( _dbContext , rightVersion ) ;
608
+ rightResourceTracked . RefreshConcurrencyValue ( ) ;
609
+ }
610
+
582
611
return rightValue is IEnumerable
583
612
? _collectionConverter . CopyToTypedCollection ( rightResourcesTracked , relationshipPropertyType )
584
613
: rightResourcesTracked . Single ( ) ;
@@ -604,7 +633,7 @@ protected virtual async Task SaveChangesAsync(CancellationToken cancellationToke
604
633
{
605
634
_dbContext . ResetChangeTracker ( ) ;
606
635
607
- throw new DataStoreUpdateException ( exception ) ;
636
+ throw exception is DbUpdateConcurrencyException ? new DataStoreConcurrencyException ( exception ) : new DataStoreUpdateException ( exception ) ;
608
637
}
609
638
}
610
639
}
0 commit comments