@@ -31,6 +31,7 @@ public class EntityFrameworkCoreRepository<TResource, TId> : IResourceRepository
31
31
where TResource : class , IIdentifiable < TId >
32
32
{
33
33
private readonly CollectionConverter _collectionConverter = new ( ) ;
34
+ private readonly IJsonApiRequest _request ;
34
35
private readonly ITargetedFields _targetedFields ;
35
36
private readonly DbContext _dbContext ;
36
37
private readonly IResourceGraph _resourceGraph ;
@@ -42,24 +43,26 @@ public class EntityFrameworkCoreRepository<TResource, TId> : IResourceRepository
42
43
/// <inheritdoc />
43
44
public virtual string ? TransactionId => _dbContext . Database . CurrentTransaction ? . TransactionId . ToString ( ) ;
44
45
45
- public EntityFrameworkCoreRepository ( ITargetedFields targetedFields , IDbContextResolver dbContextResolver , IResourceGraph resourceGraph ,
46
- IResourceFactory resourceFactory , IEnumerable < IQueryConstraintProvider > constraintProviders , ILoggerFactory loggerFactory ,
47
- IResourceDefinitionAccessor resourceDefinitionAccessor )
46
+ public EntityFrameworkCoreRepository ( IJsonApiRequest request , ITargetedFields targetedFields , IDbContextResolver dbContextResolver ,
47
+ IResourceGraph resourceGraph , IResourceFactory resourceFactory , IResourceDefinitionAccessor resourceDefinitionAccessor ,
48
+ IEnumerable < IQueryConstraintProvider > constraintProviders , ILoggerFactory loggerFactory )
48
49
{
50
+ ArgumentGuard . NotNull ( request , nameof ( request ) ) ;
49
51
ArgumentGuard . NotNull ( targetedFields , nameof ( targetedFields ) ) ;
50
52
ArgumentGuard . NotNull ( dbContextResolver , nameof ( dbContextResolver ) ) ;
51
53
ArgumentGuard . NotNull ( resourceGraph , nameof ( resourceGraph ) ) ;
52
54
ArgumentGuard . NotNull ( resourceFactory , nameof ( resourceFactory ) ) ;
55
+ ArgumentGuard . NotNull ( resourceDefinitionAccessor , nameof ( resourceDefinitionAccessor ) ) ;
53
56
ArgumentGuard . NotNull ( constraintProviders , nameof ( constraintProviders ) ) ;
54
57
ArgumentGuard . NotNull ( loggerFactory , nameof ( loggerFactory ) ) ;
55
- ArgumentGuard . NotNull ( resourceDefinitionAccessor , nameof ( resourceDefinitionAccessor ) ) ;
56
58
59
+ _request = request ;
57
60
_targetedFields = targetedFields ;
58
61
_dbContext = dbContextResolver . GetContext ( ) ;
59
62
_resourceGraph = resourceGraph ;
60
63
_resourceFactory = resourceFactory ;
61
- _constraintProviders = constraintProviders ;
62
64
_resourceDefinitionAccessor = resourceDefinitionAccessor ;
65
+ _constraintProviders = constraintProviders ;
63
66
_traceWriter = new TraceLogWriter < EntityFrameworkCoreRepository < TResource , TId > > ( loggerFactory ) ;
64
67
}
65
68
@@ -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 />
@@ -323,6 +330,7 @@ public virtual async Task DeleteAsync(TId id, CancellationToken cancellationToke
323
330
// If so, we'll reuse the tracked resource instead of this placeholder resource.
324
331
var placeholderResource = _resourceFactory . CreateInstance < TResource > ( ) ;
325
332
placeholderResource . Id = id ;
333
+ placeholderResource . RestoreConcurrencyToken ( _dbContext , _request . PrimaryVersion ) ;
326
334
327
335
await _resourceDefinitionAccessor . OnWritingAsync ( placeholderResource , WriteOperationKind . DeleteResource , cancellationToken ) ;
328
336
@@ -502,6 +510,17 @@ public virtual async Task RemoveFromToManyRelationshipAsync(TResource leftResour
502
510
503
511
if ( ! rightResourceIdsToStore . SetEquals ( rightResourceIdsStored ) )
504
512
{
513
+ if ( relationship . RightType . IsVersioned )
514
+ {
515
+ foreach ( IIdentifiable rightResource in rightResourceIdsStored )
516
+ {
517
+ string ? requestVersion = rightResourceIdsToRemove . Single ( resource => resource . StringId == rightResource . StringId ) . GetVersion ( ) ;
518
+
519
+ rightResource . RestoreConcurrencyToken ( _dbContext , requestVersion ) ;
520
+ rightResource . RefreshConcurrencyValue ( ) ;
521
+ }
522
+ }
523
+
505
524
AssertIsNotClearingRequiredToOneRelationship ( relationship , leftResourceTracked , rightResourceIdsToStore ) ;
506
525
507
526
await UpdateRelationshipAsync ( relationship , leftResourceTracked , rightResourceIdsToStore , cancellationToken ) ;
@@ -535,6 +554,9 @@ protected async Task UpdateRelationshipAsync(RelationshipAttribute relationship,
535
554
await entityEntry . Reference ( inversePropertyName ) . LoadAsync ( cancellationToken ) ;
536
555
}
537
556
557
+ leftResource . RestoreConcurrencyToken ( _dbContext , _request . PrimaryVersion ) ;
558
+ leftResource . RefreshConcurrencyValue ( ) ;
559
+
538
560
relationship . SetValue ( leftResource , trackedValueToAssign ) ;
539
561
}
540
562
@@ -548,6 +570,13 @@ protected async Task UpdateRelationshipAsync(RelationshipAttribute relationship,
548
570
ICollection < IIdentifiable > rightResources = _collectionConverter . ExtractResources ( rightValue ) ;
549
571
IIdentifiable [ ] rightResourcesTracked = rightResources . Select ( rightResource => _dbContext . GetTrackedOrAttach ( rightResource ) ) . ToArray ( ) ;
550
572
573
+ foreach ( var rightResourceTracked in rightResourcesTracked )
574
+ {
575
+ string ? rightVersion = rightResourceTracked . GetVersion ( ) ;
576
+ rightResourceTracked . RestoreConcurrencyToken ( _dbContext , rightVersion ) ;
577
+ rightResourceTracked . RefreshConcurrencyValue ( ) ;
578
+ }
579
+
551
580
return rightValue is IEnumerable
552
581
? _collectionConverter . CopyToTypedCollection ( rightResourcesTracked , relationshipPropertyType )
553
582
: rightResourcesTracked . Single ( ) ;
@@ -573,7 +602,7 @@ protected virtual async Task SaveChangesAsync(CancellationToken cancellationToke
573
602
{
574
603
_dbContext . ResetChangeTracker ( ) ;
575
604
576
- throw new DataStoreUpdateException ( exception ) ;
605
+ throw exception is DbUpdateConcurrencyException ? new DataStoreConcurrencyException ( exception ) : new DataStoreUpdateException ( exception ) ;
577
606
}
578
607
}
579
608
}
0 commit comments