feat(notification-outbox): add AddNotificationOutbox DI registration
This commit is contained in:
@@ -31,7 +31,6 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
private readonly IServiceProvider _serviceProvider;
|
||||
private readonly NotificationOutboxOptions _options;
|
||||
private readonly ILogger<NotificationOutboxActor> _logger;
|
||||
private readonly IReadOnlyDictionary<NotificationType, INotificationDeliveryAdapter> _adapters;
|
||||
|
||||
/// <summary>
|
||||
/// In-flight guard for the dispatch loop. Set true at the start of a sweep and cleared
|
||||
@@ -46,13 +45,11 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
public NotificationOutboxActor(
|
||||
IServiceProvider serviceProvider,
|
||||
NotificationOutboxOptions options,
|
||||
ILogger<NotificationOutboxActor> logger,
|
||||
IReadOnlyDictionary<NotificationType, INotificationDeliveryAdapter> adapters)
|
||||
ILogger<NotificationOutboxActor> logger)
|
||||
{
|
||||
_serviceProvider = serviceProvider;
|
||||
_options = options;
|
||||
_logger = logger;
|
||||
_adapters = adapters;
|
||||
|
||||
Receive<NotificationSubmit>(HandleSubmit);
|
||||
Receive<InternalMessages.IngestPersisted>(HandleIngestPersisted);
|
||||
@@ -174,6 +171,11 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
/// in a try/catch so the returned task never faults — scope creation, service resolution,
|
||||
/// and retry-policy resolution can all throw, and a faulted task would otherwise leave
|
||||
/// the dispatcher's in-flight guard stuck and wedge the loop permanently.
|
||||
///
|
||||
/// The channel delivery adapters are resolved from the per-sweep scope, not held in a
|
||||
/// field: <see cref="EmailNotificationDeliveryAdapter"/> takes a scoped
|
||||
/// <see cref="INotificationRepository"/> directly, so a long-lived adapter reference on
|
||||
/// this singleton actor would be a captive dependency over a disposed DbContext.
|
||||
/// </summary>
|
||||
private async Task RunDispatchPass(DateTimeOffset now)
|
||||
{
|
||||
@@ -182,6 +184,7 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var outboxRepository = scope.ServiceProvider.GetRequiredService<INotificationOutboxRepository>();
|
||||
var notificationRepository = scope.ServiceProvider.GetRequiredService<INotificationRepository>();
|
||||
var adapters = ResolveAdapters(scope.ServiceProvider);
|
||||
|
||||
IReadOnlyList<Notification> due;
|
||||
try
|
||||
@@ -205,7 +208,7 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
{
|
||||
try
|
||||
{
|
||||
await DeliverOneAsync(notification, now, maxRetries, retryDelay, outboxRepository);
|
||||
await DeliverOneAsync(notification, now, maxRetries, retryDelay, outboxRepository, adapters);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -238,6 +241,24 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
: (configuration.MaxRetries, configuration.RetryDelay);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Builds the <see cref="NotificationType"/> → adapter lookup for a dispatch sweep from
|
||||
/// the registered <see cref="INotificationDeliveryAdapter"/> services in the supplied
|
||||
/// scope. The last adapter registered for a given type wins, mirroring DI's last-wins
|
||||
/// resolution semantics.
|
||||
/// </summary>
|
||||
private static IReadOnlyDictionary<NotificationType, INotificationDeliveryAdapter> ResolveAdapters(
|
||||
IServiceProvider scopedServices)
|
||||
{
|
||||
var adapters = new Dictionary<NotificationType, INotificationDeliveryAdapter>();
|
||||
foreach (var adapter in scopedServices.GetServices<INotificationDeliveryAdapter>())
|
||||
{
|
||||
adapters[adapter.Type] = adapter;
|
||||
}
|
||||
|
||||
return adapters;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Delivers a single notification through its channel adapter and applies the resulting
|
||||
/// status transition. A missing adapter parks the notification; otherwise the
|
||||
@@ -248,9 +269,10 @@ public class NotificationOutboxActor : ReceiveActor, IWithTimers
|
||||
DateTimeOffset now,
|
||||
int maxRetries,
|
||||
TimeSpan retryDelay,
|
||||
INotificationOutboxRepository outboxRepository)
|
||||
INotificationOutboxRepository outboxRepository,
|
||||
IReadOnlyDictionary<NotificationType, INotificationDeliveryAdapter> adapters)
|
||||
{
|
||||
if (!_adapters.TryGetValue(notification.Type, out var adapter))
|
||||
if (!adapters.TryGetValue(notification.Type, out var adapter))
|
||||
{
|
||||
notification.Status = NotificationStatus.Parked;
|
||||
notification.LastError = $"no delivery adapter for type {notification.Type}";
|
||||
|
||||
Reference in New Issue
Block a user