diff --git a/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/DigitalData.MessagingService.Client.DependencyInjection.csproj b/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/DigitalData.MessagingService.Client.DependencyInjection.csproj index 4bfdc43..e4263f8 100644 --- a/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/DigitalData.MessagingService.Client.DependencyInjection.csproj +++ b/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/DigitalData.MessagingService.Client.DependencyInjection.csproj @@ -21,6 +21,8 @@ + + @@ -31,4 +33,9 @@ + + + + + diff --git a/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/EmailSender.cs b/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/EmailSender.cs new file mode 100644 index 0000000..1bd51a3 --- /dev/null +++ b/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/EmailSender.cs @@ -0,0 +1,95 @@ +using DigitalData.MessagingService.Publisher; +using DigitalData.MessagingService.Publisher.Abstraction; +using DigitalData.MessagingService.RabbitMQ; +using Microsoft.Extensions.DependencyInjection; + +namespace DigitalData.MessagingService.Client.DependencyInjection; + +/// +/// Provides a static, self-contained client for sending emails via RabbitMQ +/// without requiring a host-level dependency injection container. +/// +/// +/// This class manages its own internal using a +/// pattern so the DI container is only built once, +/// on the first call to . +/// +public static class EmailSender +{ + /// + /// Internal event used to accumulate service registrations before the + /// is built. Handlers are added by + /// and invoked exactly once during + /// lazy initialization. + /// + private static event Action ConfigureServices = delegate { }; + + /// + /// The lazily-initialized internal service provider. + /// Built on first access by invoking all registered + /// handlers. + /// + private static readonly Lazy LazyProvider = new(() => + { + var services = new ServiceCollection(); + ConfigureServices?.Invoke(services); + return services.BuildServiceProvider(); + }); + + /// + /// Gets a value indicating whether the messaging service has been connected + /// and the internal has been initialized. + /// + /// + /// if has been called + /// and the provider is built; otherwise . + /// + public static bool IsConnected => LazyProvider.IsValueCreated; + + /// + /// Configures and establishes a connection to RabbitMQ, then initializes + /// the internal dependency injection container. + /// + /// + /// A delegate used to configure the , + /// such as host, port, credentials, and exchange settings. + /// + /// + /// Controls the behavior when this method is called while already connected. + /// Defaults to . + /// + /// + /// Thrown when the service is already connected and + /// is . + /// + public static void ConnectRabbitMq(Action configure, OnReconnect onReconnect = OnReconnect.ThrowException) + { + if(IsConnected && onReconnect == OnReconnect.ThrowException) + throw new InvalidOperationException("Messaging service is already connected."); + + ConfigureServices += services => services.AddMessagingServicePublisher(configure); + _ = LazyProvider.Value; // Force initialization + } + + /// + /// Enqueues the specified email event to the RabbitMQ messaging pipeline. + /// + /// The outgoing email event to enqueue. + /// + /// Thrown when has not been called prior to sending. + /// + /// + /// This method resolves from the internal + /// service provider and calls EnqueueAsync in a fire-and-forget manner. + /// Ensure that any unhandled exceptions from the async operation are handled + /// at the publisher level. + /// + public static void Send(OutgoingEmailEvent email) + { + if(!IsConnected) + throw new InvalidOperationException("Messaging service is not connected. Call ConnectRabbitMq first."); + + var publisher = LazyProvider.Value.GetRequiredService(); + publisher.EnqueueAsync(email); + } +} \ No newline at end of file diff --git a/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/OnReconnect.cs b/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/OnReconnect.cs new file mode 100644 index 0000000..9c866e4 --- /dev/null +++ b/src/presentation/DigitalData.MessagingService.Client.DependencyInjection/OnReconnect.cs @@ -0,0 +1,20 @@ +namespace DigitalData.MessagingService.Client.DependencyInjection; + +/// +/// Defines the behavior when is called +/// while a connection has already been established. +/// +public enum OnReconnect +{ + /// + /// Throws an if the messaging service + /// is already connected. This is the default behavior. + /// + ThrowException, + + /// + /// Silently ignores the reconnection attempt if the messaging service + /// is already connected. + /// + Ignore +} \ No newline at end of file