/// <summary> /// Publishes the specified event with async/await. We expect zero or more handlers. The events are handled synchronously and concurrently /// Because any pipeline might throw, yet we want to execute the remaining handler chains, we catch exceptions on any publisher /// instead of stopping at the first failure and then we throw an AggregateException if any of the handlers failed, /// with the InnerExceptions property containing the failures. /// It is up the implementer of the handler that throws to take steps to make it easy to identify the handler that threw. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="event">The event.</param> /// <param name="continueOnCapturedContext">Should we use the calling thread's synchronization context when continuing or a default thread synchronization context. Defaults to false</param> /// <param name="cancellationToken">Allows the sender to cancel the request pipeline. Optional</param> /// <returns>awaitable <see cref="Task"/>.</returns> public async Task PublishAsync <T>(T @event, bool continueOnCapturedContext = false, CancellationToken cancellationToken = default(CancellationToken)) where T : class, IRequest { if (_handlerFactoryAsync == null) { throw new InvalidOperationException("No async handler factory defined."); } var requestContext = _requestContextFactory.Create(); requestContext.Policies = _policyRegistry; requestContext.FeatureSwitches = _featureSwitchRegistry; using (var builder = new PipelineBuilder <T>(_subscriberRegistry, _handlerFactoryAsync, _inboxConfiguration)) { s_logger.LogInformation("Building send async pipeline for event: {EventType} {Id}", @event.GetType(), @event.Id); var handlerChain = builder.BuildAsync(requestContext, continueOnCapturedContext); var handlerCount = handlerChain.Count(); s_logger.LogInformation("Found {0} async pipelines for event: {EventType} {Id}", handlerCount, @event.GetType(), @event.Id); var exceptions = new List <Exception>(); foreach (var handler in handlerChain) { try { await handler.HandleAsync(@event, cancellationToken).ConfigureAwait(continueOnCapturedContext); } catch (Exception e) { exceptions.Add(e); } } if (exceptions.Count > 0) { throw new AggregateException("Failed to async publish to one more handlers successfully, see inner exceptions for details", exceptions); } } }
/// <summary> /// Awaitably sends the specified command. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="command">The command.</param> /// <param name="continueOnCapturedContext">Should we use the calling thread's synchronization context when continuing or a default thread synchronization context. Defaults to false</param> /// <param name="cancellationToken">Allows the sender to cancel the request pipeline. Optional</param> /// <returns>awaitable <see cref="Task"/>.</returns> public async Task SendAsync <T>(T command, bool continueOnCapturedContext = false, CancellationToken cancellationToken = default(CancellationToken)) where T : class, IRequest { if (_asyncHandlerFactory == null) { throw new InvalidOperationException("No async handler factory defined."); } var requestContext = _requestContextFactory.Create(); requestContext.Policies = _policyRegistry; using (var builder = new PipelineBuilder <T>(_subscriberRegistry, _asyncHandlerFactory)) { _logger.Value.InfoFormat("Building send async pipeline for command: {0} {1}", command.GetType(), command.Id); var handlerChain = builder.BuildAsync(requestContext, continueOnCapturedContext); AssertValidSendPipeline(command, handlerChain.Count()); await handlerChain.First().HandleAsync(command, cancellationToken).ConfigureAwait(continueOnCapturedContext); } }
/// <summary> /// Sends the specified command. We expect only one handler. The command is handled synchronously. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="command">The command.</param> /// <exception cref="System.ArgumentException"> /// </exception> public void Send <T>(T command) where T : class, IRequest { if (_handlerFactory == null) { throw new InvalidOperationException("No handler factory defined."); } var requestContext = _requestContextFactory.Create(); requestContext.Policies = _policyRegistry; using (var builder = new PipelineBuilder <T>(_subscriberRegistry, _handlerFactory)) { _logger.Value.InfoFormat("Building send pipeline for command: {0} {1}", command.GetType(), command.Id); var handlerChain = builder.Build(requestContext); AssertValidSendPipeline(command, handlerChain.Count()); handlerChain.First().Handle(command); } }
/// <summary> /// Publishes the specified event. We expect zero or more handlers. The events are handled synchronously, in turn /// Because any pipeline might throw, yet we want to execute the remaining handler chains, we catch exceptions on any publisher /// instead of stopping at the first failure and then we throw an AggregateException if any of the handlers failed, /// with the InnerExceptions property containing the failures. /// It is up the implementer of the handler that throws to take steps to make it easy to identify the handler that threw. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="event">The event.</param> public void Publish <T>(T @event) where T : class, IRequest { if (_handlerFactory == null) { throw new InvalidOperationException("No handler factory defined."); } var requestContext = _requestContextFactory.Create(); requestContext.Policies = _policyRegistry; requestContext.FeatureSwitches = _featureSwitchRegistry; using (var builder = new PipelineBuilder <T>(_subscriberRegistry, _handlerFactory, _inboxConfiguration)) { _logger.Value.InfoFormat("Building send pipeline for event: {0} {1}", @event.GetType(), @event.Id); var handlerChain = builder.Build(requestContext); var handlerCount = handlerChain.Count(); _logger.Value.InfoFormat("Found {0} pipelines for event: {1} {2}", handlerCount, @event.GetType(), @event.Id); var exceptions = new List <Exception>(); foreach (var handleRequests in handlerChain) { try { handleRequests.Handle(@event); } catch (Exception e) { exceptions.Add(e); } } if (exceptions.Any()) { throw new AggregateException("Failed to publish to one more handlers successfully, see inner exceptions for details", exceptions); } } }
/// <summary> /// Sends the specified command. We expect only one handler. The command is handled synchronously. /// </summary> /// <typeparam name="T"></typeparam> /// <param name="command">The command.</param> /// <exception cref="System.ArgumentException"> /// </exception> public void Send <T>(T command) where T : class, IRequest { if (_handlerFactorySync == null) { throw new InvalidOperationException("No handler factory defined."); } var requestContext = _requestContextFactory.Create(); requestContext.Policies = _policyRegistry; requestContext.FeatureSwitches = _featureSwitchRegistry; using (var builder = new PipelineBuilder <T>(_subscriberRegistry, _handlerFactorySync, _inboxConfiguration)) { s_logger.LogInformation("Building send pipeline for command: {CommandType} {Id}", command.GetType(), command.Id); var handlerChain = builder.Build(requestContext); AssertValidSendPipeline(command, handlerChain.Count()); handlerChain.First().Handle(command); } }