Skip to content

Instantly share code, notes, and snippets.

@joacar
Last active October 15, 2019 13:23
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save joacar/273d84da5219bf57c8d47d1a59cff68f to your computer and use it in GitHub Desktop.
Save joacar/273d84da5219bf57c8d47d1a59cff68f to your computer and use it in GitHub Desktop.
NServiceBus shared service provider attempt
namespace Nordax.NServiceBus {
/// <summary>
/// Behavior that wraps invocation of pipeline with a scoped service provider.
/// </summary>
internal sealed class ScopedServiceBehavior : Behavior<IIncomingPhysicalMessageContext>
{
/// <summary>
/// Registered behavior with name.
/// </summary>
public const string StepId = "ScopedService";
private readonly IServiceScopeFactory _serviceScopeFactory;
public ScopedServiceBehavior(IServiceScopeFactory serviceScopeFactory)
{
_serviceScopeFactory = serviceScopeFactory;
}
public override async Task Invoke(IIncomingPhysicalMessageContext context, Func<Task> next)
{
using (var scope = _serviceScopeFactory.CreateScope())
{
// Previously implementation injected in handler constructor is now available via
// context.GetService(typeof(T));
// All resolved services of T (if registered using AddScoped()) in the pipeline
// will yield same instance causing it to be shared amongst handlers and behaviors.
context.Extensions.Set(scope);
await next().ConfigureAwait(false);
}
}
}
internal sealed class EventDispatcherBehavior : Behavior<IInvokeHandlerContext>
{
private readonly IEventDispatcherFactory _dispatcherFactory;
private readonly ILogger<EventDispatcherBehavior> _logger;
private readonly EventDispatcherSettings _settings;
public EventDispatcherBehavior(
ILoggerFactory loggerFactory,
EventDispatcherSettings settings)
{
_settings = settings ?? throw new ArgumentNullException(nameof(settings));
_dispatcherFactory = settings.DispatcherFactory ?? throw new ArgumentNullException(nameof(settings.DispatcherFactory));
_logger = loggerFactory.CreateLogger<EventDispatcherBehavior>();
}
public override async Task Invoke(IInvokeHandlerContext context, Func<Task> next)
{
// Execute this outside of try-catch so errors are propagated in default way
await next().ConfigureAwait(false);
try
{
// Check if available in scoped service
var eventProvider = context.GetService<IEventProvider>();
if(eventProvider != null && eventProvider.HasEvents)
{
// We have the events!
var events = eventProvider.GetEvents();
var dispatcher = _dispatcherFactory.CreateFromHandlerContext(context);
foreach (var @event in events)
{
if (@event is null)
{
continue;
}
// Default implementation just wraps context as sender
await dispatcher.Dispatch(@event).ConfigureAwait(false);
}
}
}
catch (Exception e)
{
// Error handling
}
}
}
internal sealed class Handler : IHandleMessages<Command>
{
private readonly ILogger<Handler> _logger;
private readonly CommandUseCase _useCase;
public Handler(
ILoggerFactory loggerFactory,
CommandUseCase commandUseCase)
{
_logger = loggerFactory.CreateLogger<Handler>();
_useCase = commandUseCase;
}
public async Task Handle(Command message, IMessageHandlerContext context)
{
// Resolved from ASPNET DI
var useCase = context.GetService<CommandUseCase>();
// _useCase is resolved NSB DI
Debug.Assert(ReferenceEquals(useCase, _useCase), "");
await useCase.Execute().ConfigureAwait(false);
}
}
public sealed class CommandUseCase
{
private readonly IEventCollectionPublisher _eventCollectionPublisher;
public CommandUseCase(IEventCollectionPublisher eventCollectionPublisher)
{
_eventCollectionPublisher = eventCollectionPublisher;
}
public Task Execute()
{
// Do domain stuff
var customer = new Customer();
// Publish domain events
_eventCollectionPublisher.Publish(customer.Events);
return Task.CompletedTask;
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment