Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

170 строки
5.8KB

  1. using System.Collections.Concurrent;
  2. namespace Campaign_Tracker.Server.Municipalities;
  3. public sealed class InMemoryMunicipalityAddressRepository : IMunicipalityAddressRepository
  4. {
  5. private readonly ConcurrentDictionary<string, MunicipalityAddress> _addresses =
  6. new(StringComparer.OrdinalIgnoreCase);
  7. private readonly object _lock = new();
  8. private readonly TimeProvider _timeProvider;
  9. public InMemoryMunicipalityAddressRepository(TimeProvider timeProvider)
  10. {
  11. _timeProvider = timeProvider;
  12. }
  13. public Task<IReadOnlyList<MunicipalityAddress>> GetByProfileIdAsync(
  14. string profileId,
  15. CancellationToken cancellationToken = default)
  16. {
  17. var result = _addresses.Values
  18. .Where(a => a.ProfileId == profileId && !a.IsDeleted)
  19. .OrderByDescending(a => a.EffectiveDate)
  20. .ToArray();
  21. return Task.FromResult<IReadOnlyList<MunicipalityAddress>>(result);
  22. }
  23. public Task<MunicipalityAddress?> GetByIdAsync(
  24. string addressId,
  25. CancellationToken cancellationToken = default)
  26. {
  27. _addresses.TryGetValue(addressId, out var address);
  28. return Task.FromResult(address is { IsDeleted: false } ? address : null);
  29. }
  30. public Task<MunicipalityAddressSaveResult> AddAsync(
  31. string profileId,
  32. string addressType,
  33. string street,
  34. string city,
  35. string state,
  36. string zipCode,
  37. DateTimeOffset effectiveDate,
  38. string actorIdentity,
  39. CancellationToken cancellationToken = default)
  40. {
  41. var now = _timeProvider.GetUtcNow();
  42. lock (_lock)
  43. {
  44. // Per-type history: mark existing current address of same type as not-current
  45. foreach (var existing in _addresses.Values
  46. .Where(a => a.ProfileId == profileId && a.AddressType == addressType
  47. && a.IsCurrent && !a.IsDeleted))
  48. {
  49. _addresses[existing.AddressId] = existing with
  50. {
  51. IsCurrent = false,
  52. UpdatedAt = now,
  53. UpdatedBy = actorIdentity
  54. };
  55. }
  56. var address = new MunicipalityAddress(
  57. AddressId: Guid.NewGuid().ToString("N"),
  58. ProfileId: profileId,
  59. AddressType: addressType,
  60. Street: street,
  61. City: city,
  62. State: state,
  63. ZipCode: zipCode,
  64. EffectiveDate: effectiveDate,
  65. IsCurrent: true,
  66. IsDeleted: false,
  67. CreatedAt: now,
  68. CreatedBy: actorIdentity,
  69. UpdatedAt: now,
  70. UpdatedBy: actorIdentity);
  71. _addresses[address.AddressId] = address;
  72. return Task.FromResult(MunicipalityAddressSaveResult.Success(address));
  73. }
  74. }
  75. public Task<MunicipalityAddressSaveResult> UpdateAsync(
  76. string addressId,
  77. string addressType,
  78. string street,
  79. string city,
  80. string state,
  81. string zipCode,
  82. DateTimeOffset effectiveDate,
  83. string actorIdentity,
  84. CancellationToken cancellationToken = default)
  85. {
  86. var now = _timeProvider.GetUtcNow();
  87. lock (_lock)
  88. {
  89. if (!_addresses.TryGetValue(addressId, out var existing) || existing.IsDeleted)
  90. return Task.FromResult(MunicipalityAddressSaveResult.NotFound(addressId));
  91. // Preserve the old record in history by marking it not-current
  92. _addresses[addressId] = existing with
  93. {
  94. IsCurrent = false,
  95. UpdatedAt = now,
  96. UpdatedBy = actorIdentity
  97. };
  98. // Mark any other current addresses of the same type as not-current
  99. foreach (var other in _addresses.Values
  100. .Where(a => a.ProfileId == existing.ProfileId && a.AddressType == addressType
  101. && a.IsCurrent && a.AddressId != addressId && !a.IsDeleted))
  102. {
  103. _addresses[other.AddressId] = other with
  104. {
  105. IsCurrent = false,
  106. UpdatedAt = now,
  107. UpdatedBy = actorIdentity
  108. };
  109. }
  110. // Insert a new current record (history-preserving update)
  111. var updated = new MunicipalityAddress(
  112. AddressId: Guid.NewGuid().ToString("N"),
  113. ProfileId: existing.ProfileId,
  114. AddressType: addressType,
  115. Street: street,
  116. City: city,
  117. State: state,
  118. ZipCode: zipCode,
  119. EffectiveDate: effectiveDate,
  120. IsCurrent: true,
  121. IsDeleted: false,
  122. CreatedAt: now,
  123. CreatedBy: actorIdentity,
  124. UpdatedAt: now,
  125. UpdatedBy: actorIdentity);
  126. _addresses[updated.AddressId] = updated;
  127. return Task.FromResult(MunicipalityAddressSaveResult.Success(updated));
  128. }
  129. }
  130. public Task<MunicipalityAddressSaveResult> SoftDeleteAsync(
  131. string addressId,
  132. string actorIdentity,
  133. CancellationToken cancellationToken = default)
  134. {
  135. var now = _timeProvider.GetUtcNow();
  136. lock (_lock)
  137. {
  138. if (!_addresses.TryGetValue(addressId, out var existing) || existing.IsDeleted)
  139. return Task.FromResult(MunicipalityAddressSaveResult.NotFound(addressId));
  140. _addresses[addressId] = existing with
  141. {
  142. IsDeleted = true,
  143. IsCurrent = false,
  144. UpdatedAt = now,
  145. UpdatedBy = actorIdentity
  146. };
  147. return Task.FromResult(MunicipalityAddressSaveResult.Success(_addresses[addressId]));
  148. }
  149. }
  150. }

Powered by TurnKey Linux.