| | | 1 | | using System.Runtime.CompilerServices; |
| | | 2 | | using Microsoft.Extensions.Options; |
| | | 3 | | using ProjectTemplate.Infrastructure.Data.Auditing; |
| | | 4 | | using ProjectTemplate.Infrastructure.Data.Options; |
| | | 5 | | |
| | | 6 | | namespace ProjectTemplate.Infrastructure.Data; |
| | | 7 | | |
| | | 8 | | /// <summary> |
| | | 9 | | /// Routes save lifecycle callbacks to one mutable pipeline instance per application database context. |
| | | 10 | | /// </summary> |
| | | 11 | | internal sealed class ContextIsolatedApplicationSaveChangesPipeline : |
| | | 12 | | IApplicationSaveChangesPipeline, |
| | | 13 | | IApplicationMutationAuditReceiptRegistry |
| | | 14 | | { |
| | 16 | 15 | | private readonly ConditionalWeakTable<ApplicationDbContext, ApplicationSaveChangesPipeline> _pipelines = []; |
| | | 16 | | private readonly ICurrentActorAccessor _currentActorAccessor; |
| | | 17 | | private readonly IOptions<DataAccessOptions> _dataAccessOptions; |
| | | 18 | | private readonly IApplicationAuditStore? _auditStore; |
| | | 19 | | private readonly IApplicationAuditContextAccessor? _auditContextAccessor; |
| | | 20 | | private readonly IApplicationAuditValuePolicy? _auditValuePolicy; |
| | | 21 | | private readonly IApplicationMutationManifestBuilder _manifestBuilder; |
| | | 22 | | private readonly IApplicationMutationManifestHasher _manifestHasher; |
| | | 23 | | |
| | 16 | 24 | | public ContextIsolatedApplicationSaveChangesPipeline( |
| | 16 | 25 | | ICurrentActorAccessor currentActorAccessor, |
| | 16 | 26 | | IOptions<DataAccessOptions> dataAccessOptions, |
| | 16 | 27 | | IApplicationMutationManifestBuilder manifestBuilder, |
| | 16 | 28 | | IApplicationMutationManifestHasher manifestHasher, |
| | 16 | 29 | | IApplicationAuditStore? auditStore = null, |
| | 16 | 30 | | IApplicationAuditContextAccessor? auditContextAccessor = null, |
| | 16 | 31 | | IApplicationAuditValuePolicy? auditValuePolicy = null) |
| | | 32 | | { |
| | 16 | 33 | | ArgumentNullException.ThrowIfNull(currentActorAccessor); |
| | 16 | 34 | | ArgumentNullException.ThrowIfNull(dataAccessOptions); |
| | 16 | 35 | | ArgumentNullException.ThrowIfNull(manifestBuilder); |
| | 16 | 36 | | ArgumentNullException.ThrowIfNull(manifestHasher); |
| | | 37 | | |
| | 16 | 38 | | _currentActorAccessor = currentActorAccessor; |
| | 16 | 39 | | _dataAccessOptions = dataAccessOptions; |
| | 16 | 40 | | _auditStore = auditStore; |
| | 16 | 41 | | _auditContextAccessor = auditContextAccessor; |
| | 16 | 42 | | _auditValuePolicy = auditValuePolicy; |
| | 16 | 43 | | _manifestBuilder = manifestBuilder; |
| | 16 | 44 | | _manifestHasher = manifestHasher; |
| | 16 | 45 | | } |
| | | 46 | | |
| | | 47 | | public bool ApplyBeforeSaveChanges(ApplicationDbContext dbContext) |
| | | 48 | | { |
| | 0 | 49 | | ArgumentNullException.ThrowIfNull(dbContext); |
| | 0 | 50 | | return GetPipeline(dbContext).ApplyBeforeSaveChanges(dbContext); |
| | | 51 | | } |
| | | 52 | | |
| | | 53 | | public ValueTask<bool> ApplyBeforeSaveChangesAsync( |
| | | 54 | | ApplicationDbContext dbContext, |
| | | 55 | | CancellationToken cancellationToken = default) |
| | | 56 | | { |
| | 4 | 57 | | ArgumentNullException.ThrowIfNull(dbContext); |
| | 4 | 58 | | return GetPipeline(dbContext).ApplyBeforeSaveChangesAsync(dbContext, cancellationToken); |
| | | 59 | | } |
| | | 60 | | |
| | | 61 | | public bool ApplyAfterSaveChanges(ApplicationDbContext dbContext) |
| | | 62 | | { |
| | 0 | 63 | | ArgumentNullException.ThrowIfNull(dbContext); |
| | 0 | 64 | | return GetPipeline(dbContext).ApplyAfterSaveChanges(dbContext); |
| | | 65 | | } |
| | | 66 | | |
| | | 67 | | public ValueTask<bool> ApplyAfterSaveChangesAsync( |
| | | 68 | | ApplicationDbContext dbContext, |
| | | 69 | | CancellationToken cancellationToken = default) |
| | | 70 | | { |
| | 4 | 71 | | ArgumentNullException.ThrowIfNull(dbContext); |
| | 4 | 72 | | return GetPipeline(dbContext).ApplyAfterSaveChangesAsync(dbContext, cancellationToken); |
| | | 73 | | } |
| | | 74 | | |
| | | 75 | | public ApplicationMutationAuditReceipt? GetLastCompletedReceipt(ApplicationDbContext dbContext) |
| | | 76 | | { |
| | 6 | 77 | | ArgumentNullException.ThrowIfNull(dbContext); |
| | 6 | 78 | | return _pipelines.TryGetValue(dbContext, out ApplicationSaveChangesPipeline? pipeline) |
| | 6 | 79 | | ? pipeline.LastCompletedReceipt |
| | 6 | 80 | | : null; |
| | | 81 | | } |
| | | 82 | | |
| | | 83 | | private ApplicationSaveChangesPipeline GetPipeline(ApplicationDbContext dbContext) |
| | | 84 | | { |
| | 12 | 85 | | return _pipelines.GetValue(dbContext, _ => new ApplicationSaveChangesPipeline( |
| | 12 | 86 | | _currentActorAccessor, |
| | 12 | 87 | | _dataAccessOptions, |
| | 12 | 88 | | _auditStore, |
| | 12 | 89 | | _auditContextAccessor, |
| | 12 | 90 | | _auditValuePolicy, |
| | 12 | 91 | | _manifestBuilder, |
| | 12 | 92 | | _manifestHasher)); |
| | | 93 | | } |
| | | 94 | | } |