Add EmailSender static client with lazy-initialized DI container
- Add EmailSender class with ConnectRabbitMq and Send methods - Add OnReconnect enum for reconnection behavior control - Add required NuGet packages (DependencyInjection, Hosting.Abstractions) - Add project references to Publisher.Abstraction and Publisher
This commit is contained in:
@@ -21,6 +21,8 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="8.0.1" />
|
||||||
|
<PackageReference Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.10" />
|
||||||
<PackageReference Include="RabbitMQ.Client" Version="7.2.1" />
|
<PackageReference Include="RabbitMQ.Client" Version="7.2.1" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
@@ -31,4 +33,9 @@
|
|||||||
</None>
|
</None>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
|
<ItemGroup>
|
||||||
|
<ProjectReference Include="..\..\core\DigitalData.MessagingService.Publisher.Abstraction\DigitalData.MessagingService.Publisher.Abstraction.csproj" />
|
||||||
|
<ProjectReference Include="..\..\infrastructure\DigitalData.MessagingService.Publisher\DigitalData.MessagingService.Publisher.csproj" />
|
||||||
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides a static, self-contained client for sending emails via RabbitMQ
|
||||||
|
/// without requiring a host-level dependency injection container.
|
||||||
|
/// </summary>
|
||||||
|
/// <remarks>
|
||||||
|
/// This class manages its own internal <see cref="IServiceProvider"/> using a
|
||||||
|
/// <see cref="Lazy{T}"/> pattern so the DI container is only built once,
|
||||||
|
/// on the first call to <see cref="ConnectRabbitMq"/>.
|
||||||
|
/// </remarks>
|
||||||
|
public static class EmailSender
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Internal event used to accumulate service registrations before the
|
||||||
|
/// <see cref="IServiceProvider"/> is built. Handlers are added by
|
||||||
|
/// <see cref="ConnectRabbitMq"/> and invoked exactly once during
|
||||||
|
/// lazy initialization.
|
||||||
|
/// </summary>
|
||||||
|
private static event Action<IServiceCollection> ConfigureServices = delegate { };
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The lazily-initialized internal service provider.
|
||||||
|
/// Built on first access by invoking all registered
|
||||||
|
/// <see cref="ConfigureServices"/> handlers.
|
||||||
|
/// </summary>
|
||||||
|
private static readonly Lazy<IServiceProvider> LazyProvider = new(() =>
|
||||||
|
{
|
||||||
|
var services = new ServiceCollection();
|
||||||
|
ConfigureServices?.Invoke(services);
|
||||||
|
return services.BuildServiceProvider();
|
||||||
|
});
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets a value indicating whether the messaging service has been connected
|
||||||
|
/// and the internal <see cref="IServiceProvider"/> has been initialized.
|
||||||
|
/// </summary>
|
||||||
|
/// <value>
|
||||||
|
/// <see langword="true"/> if <see cref="ConnectRabbitMq"/> has been called
|
||||||
|
/// and the provider is built; otherwise <see langword="false"/>.
|
||||||
|
/// </value>
|
||||||
|
public static bool IsConnected => LazyProvider.IsValueCreated;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Configures and establishes a connection to RabbitMQ, then initializes
|
||||||
|
/// the internal dependency injection container.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="configure">
|
||||||
|
/// A delegate used to configure the <see cref="RabbitMqConfiguration"/>,
|
||||||
|
/// such as host, port, credentials, and exchange settings.
|
||||||
|
/// </param>
|
||||||
|
/// <param name="onReconnect">
|
||||||
|
/// Controls the behavior when this method is called while already connected.
|
||||||
|
/// Defaults to <see cref="OnReconnect.ThrowException"/>.
|
||||||
|
/// </param>
|
||||||
|
/// <exception cref="InvalidOperationException">
|
||||||
|
/// Thrown when the service is already connected and
|
||||||
|
/// <paramref name="onReconnect"/> is <see cref="OnReconnect.ThrowException"/>.
|
||||||
|
/// </exception>
|
||||||
|
public static void ConnectRabbitMq(Action<RabbitMqConfiguration> 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
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Enqueues the specified email event to the RabbitMQ messaging pipeline.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="email">The outgoing email event to enqueue.</param>
|
||||||
|
/// <exception cref="InvalidOperationException">
|
||||||
|
/// Thrown when <see cref="ConnectRabbitMq"/> has not been called prior to sending.
|
||||||
|
/// </exception>
|
||||||
|
/// <remarks>
|
||||||
|
/// This method resolves <see cref="IOutgoingEmailPublisher"/> from the internal
|
||||||
|
/// service provider and calls <c>EnqueueAsync</c> in a fire-and-forget manner.
|
||||||
|
/// Ensure that any unhandled exceptions from the async operation are handled
|
||||||
|
/// at the publisher level.
|
||||||
|
/// </remarks>
|
||||||
|
public static void Send(OutgoingEmailEvent email)
|
||||||
|
{
|
||||||
|
if(!IsConnected)
|
||||||
|
throw new InvalidOperationException("Messaging service is not connected. Call ConnectRabbitMq first.");
|
||||||
|
|
||||||
|
var publisher = LazyProvider.Value.GetRequiredService<IOutgoingEmailPublisher>();
|
||||||
|
publisher.EnqueueAsync(email);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
namespace DigitalData.MessagingService.Client.DependencyInjection;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Defines the behavior when <see cref="EmailSender.ConnectRabbitMq"/> is called
|
||||||
|
/// while a connection has already been established.
|
||||||
|
/// </summary>
|
||||||
|
public enum OnReconnect
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Throws an <see cref="System.InvalidOperationException"/> if the messaging service
|
||||||
|
/// is already connected. This is the default behavior.
|
||||||
|
/// </summary>
|
||||||
|
ThrowException,
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Silently ignores the reconnection attempt if the messaging service
|
||||||
|
/// is already connected.
|
||||||
|
/// </summary>
|
||||||
|
Ignore
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user