private async Task ProduceResponse(MessageWithHeaders requestMessage, string requestId, object message, object response, string responseError)
        {
            // send the response (or error response)
            _logger.LogDebug("Serializing the response {0} of type {1} for RequestId: {2}...", response, _consumerSettings.ResponseType, requestId);

            var responseMessage = new MessageWithHeaders();

            responseMessage.SetHeader(ReqRespMessageHeaders.RequestId, requestId);
            responseMessage.SetHeader(ReqRespMessageHeaders.Error, responseError);

            await _messageBus.ProduceResponse(message, requestMessage, response, responseMessage, _consumerSettings).ConfigureAwait(false);
        }
        public virtual async Task <Exception> ProcessMessage(TMessage msg)
        {
            Exception exceptionResult = null;

            try
            {
                DeserializeMessage(msg, out var requestMessage, out var requestId, out var expires, out var message);

                // Verify if the request/message is already expired
                if (expires != null)
                {
                    var currentTime = _messageBus.CurrentTime;
                    if (currentTime > expires.Value)
                    {
                        _logger.LogWarning("The message arrived too late and is already expired (expires {0}, current {1})", expires.Value, currentTime);

                        try
                        {
                            // Execute the event hook
                            _consumerSettings.OnMessageExpired?.Invoke(_messageBus, _consumerSettings, message, msg);
                            _messageBus.Settings.OnMessageExpired?.Invoke(_messageBus, _consumerSettings, message, msg);
                        }
                        catch (Exception eh)
                        {
                            MessageBusBase.HookFailed(_logger, eh, nameof(IConsumerEvents.OnMessageExpired));
                        }

                        // Do not process the expired message
                        return(null);
                    }
                }

                object response      = null;
                string responseError = null;

                var consumerInstance = await _instancesQueue.ReceiveAsync(_messageBus.CancellationToken).ConfigureAwait(false);

                try
                {
                    if (_consumerWithContext && _consumerContextInitializer != null)
                    {
                        var consumerContext = new ConsumerContext();
                        _consumerContextInitializer(msg, consumerContext);

                        var consumerWithContext = (IConsumerContextAware)consumerInstance;
                        consumerWithContext.Context.Value = consumerContext;
                    }

                    try
                    {
                        // Execute the event hook
                        _consumerSettings.OnMessageArrived?.Invoke(_messageBus, _consumerSettings, message, _consumerSettings.Topic, msg);
                        _messageBus.Settings.OnMessageArrived?.Invoke(_messageBus, _consumerSettings, message, _consumerSettings.Topic, msg);
                    }
                    catch (Exception eh)
                    {
                        MessageBusBase.HookFailed(_logger, eh, nameof(IConsumerEvents.OnMessageArrived));
                    }

                    // the consumer just subscribes to the message
                    var task = _consumerSettings.ConsumerMethod(consumerInstance, message, _consumerSettings.Topic);
                    await task.ConfigureAwait(false);

                    if (_consumerSettings.ConsumerMode == ConsumerMode.RequestResponse)
                    {
                        // the consumer handles the request (and replies)
                        response = _consumerSettings.ConsumerMethodResult(task);
                    }
                }
                catch (Exception e)
                {
                    if (_consumerSettings.ConsumerMode == ConsumerMode.RequestResponse)
                    {
                        _logger.LogError(e, "Handler execution failed");
                        // Save the exception
                        responseError = e.ToString();
                    }
                    else
                    {
                        _logger.LogError(e, "Consumer execution failed");
                    }

                    try
                    {
                        // Execute the event hook
                        _consumerSettings.OnMessageFault?.Invoke(_messageBus, _consumerSettings, message, e, msg);
                        _messageBus.Settings.OnMessageFault?.Invoke(_messageBus, _consumerSettings, message, e, msg);
                    }
                    catch (Exception eh)
                    {
                        MessageBusBase.HookFailed(_logger, eh, nameof(IConsumerEvents.OnMessageFault));
                    }

                    exceptionResult = e;
                }
                finally
                {
                    await _instancesQueue.SendAsync(consumerInstance).ConfigureAwait(false);
                }

                if (response != null || responseError != null)
                {
                    // send the response (or error response)
                    _logger.LogDebug("Serializing the response {0} of type {1} for RequestId: {2}...", response, _consumerSettings.ResponseType, requestId);

                    var responseMessage = new MessageWithHeaders();
                    responseMessage.SetHeader(ReqRespMessageHeaders.RequestId, requestId);
                    responseMessage.SetHeader(ReqRespMessageHeaders.Error, responseError);

                    await _messageBus.ProduceResponse(message, requestMessage, response, responseMessage, _consumerSettings).ConfigureAwait(false);
                }
            }
            catch (Exception e)
            {
                _logger.LogError(e, "Processing of the message {0} of type {1} failed", msg, _consumerSettings.MessageType);
                exceptionResult = e;
            }
            return(exceptionResult);
        }
        public virtual async Task ProcessMessage(TMessage msg)
        {
            var msgPayload = _messagePayloadProvider(msg);

            MessageWithHeaders requestMessage = null;
            string             requestId      = null;
            DateTimeOffset?    expires        = null;

            _log.Debug("Deserializing message...");
            var message = _consumerSettings.IsRequestMessage
                ? _messageBus.DeserializeRequest(_consumerSettings.MessageType, msgPayload, out requestMessage)
                : _messageBus.Settings.Serializer.Deserialize(_consumerSettings.MessageType, msgPayload);

            if (requestMessage != null)
            {
                requestMessage.TryGetHeader(ReqRespMessageHeaders.RequestId, out requestId);
                requestMessage.TryGetHeader(ReqRespMessageHeaders.Expires, out expires);
            }

            // Verify if the request/message is already expired
            if (expires.HasValue)
            {
                var currentTime = _messageBus.CurrentTime;
                if (currentTime > expires.Value)
                {
                    _log.WarnFormat(CultureInfo.InvariantCulture, "The message arrived too late and is already expired (expires {0}, current {1})", expires.Value, currentTime);

                    try
                    {
                        // Execute the event hook
                        (_consumerSettings.OnMessageExpired ?? _messageBus.Settings.OnMessageExpired)?.Invoke(_consumerSettings, message);
                    }
                    catch (Exception eh)
                    {
                        // When the hook itself error out, catch the exception
                        _log.ErrorFormat(CultureInfo.InvariantCulture, "{0} method failed", eh, nameof(IConsumerEvents.OnMessageExpired));
                    }

                    // Do not process the expired message
                    return;
                }
            }

            object response      = null;
            string responseError = null;

            var consumerInstance = await _instancesQueue.ReceiveAsync(_messageBus.CancellationToken).ConfigureAwait(false);

            try
            {
                if (_consumerWithContext && _consumerContextInitializer != null)
                {
                    var consumerContext = new ConsumerContext();
                    _consumerContextInitializer(msg, consumerContext);

                    var consumerWithContext = (IConsumerContextAware)consumerInstance;
                    consumerWithContext.Context.Value = consumerContext;
                }

                // the consumer just subscribes to the message
                var task = _consumerRuntimeInfo.OnHandle(consumerInstance, message);
                await task.ConfigureAwait(false);

                if (_consumerSettings.ConsumerMode == ConsumerMode.RequestResponse)
                {
                    // the consumer handles the request (and replies)
                    response = _consumerRuntimeInfo.GetResponseValue(task);
                }
            }
            catch (Exception e)
            {
                if (_consumerSettings.ConsumerMode == ConsumerMode.RequestResponse)
                {
                    _log.ErrorFormat(CultureInfo.InvariantCulture, "Handler execution failed", e);
                    // Save the exception
                    responseError = e.ToString();
                }
                else
                {
                    _log.ErrorFormat(CultureInfo.InvariantCulture, "Consumer execution failed", e);
                }

                try
                {
                    // Execute the event hook
                    (_consumerSettings.OnMessageFault ?? _messageBus.Settings.OnMessageFault)?.Invoke(_consumerSettings, message, e);
                }
                catch (Exception eh)
                {
                    // When the hook itself error out, catch the exception
                    _log.ErrorFormat(CultureInfo.InvariantCulture, "{0} method failed", eh, nameof(IConsumerEvents.OnMessageFault));
                }
            }
            finally
            {
                await _instancesQueue.SendAsync(consumerInstance).ConfigureAwait(false);
            }

            if (response != null || responseError != null)
            {
                // send the response (or error response)
                _log.DebugFormat(CultureInfo.InvariantCulture, "Serializing the response {0} of type {1} for RequestId: {2}...", response, _consumerSettings.ResponseType, requestId);

                var responseMessage = new MessageWithHeaders();
                responseMessage.SetHeader(ReqRespMessageHeaders.RequestId, requestId);
                responseMessage.SetHeader(ReqRespMessageHeaders.Error, responseError);

                await _messageBus.ProduceResponse(message, requestMessage, response, responseMessage, _consumerSettings).ConfigureAwait(false);
            }
        }