/// <summary>
 /// Asynchronously publishes the specified message to a bus.
 /// </summary>
 /// <typeparam name="TMessage">
 /// The type of the message to publish.
 /// </typeparam>
 /// <param name="message">
 /// The message to publish.
 /// </param>
 /// <param name="entityType">
 /// The targeted entity type.
 /// </param>
 /// <param name="controlToken">
 /// A token that ensures thread safety for the operation.
 /// </param>
 /// <returns>
 /// A task representing the asynchronous operation.
 /// </returns>
 protected sealed override async Task PublishAsync <TMessage>(TMessage message, MessagingEntityType entityType, ConcurrencyControlToken controlToken)
 {
     var messageType            = typeof(TMessage);
     var serializedMessage      = SerializeMessage(message);
     var azureServiceBusMessage = AzureServiceBusClientManager.CreateAzureServiceBusMessage(serializedMessage, messageType, MessageSerializationFormat, message.Identifier, message.CorrelationIdentifier);
     var sendClient             = ClientManager.GetSendClient <TMessage>(entityType);
     await sendClient.SendAsync(azureServiceBusMessage).ConfigureAwait(false);
 }
        /// <summary>
        /// Registers the specified message handler with the bus.
        /// </summary>
        /// <typeparam name="TRequestMessage">
        /// The type of the request message.
        /// </typeparam>
        /// <typeparam name="TResponseMessage">
        /// The type of the response message.
        /// </typeparam>
        /// <param name="requestMessageHandler">
        /// A function that handles a request message.
        /// </param>
        /// <param name="requestMessageEntityType">
        /// The entity type that is used for publishing and subscribing to request messages.
        /// </param>
        /// <param name="responseMessageEntityType">
        /// The entity type that is used for publishing and subscribing to response messages.
        /// </param>
        /// <param name="controlToken">
        /// A token that ensures thread safety for the operation.
        /// </param>
        protected sealed override void RegisterHandler <TRequestMessage, TResponseMessage>(Func <TRequestMessage, TResponseMessage> requestMessageHandler, MessagingEntityType requestMessageEntityType, MessagingEntityType responseMessageEntityType, ConcurrencyControlToken controlToken)
        {
            var responseMessageType  = typeof(TResponseMessage);
            var requestReceiveClient = ClientManager.GetReceiveClient <TRequestMessage>(requestMessageEntityType);
            var responseSendClient   = ClientManager.GetSendClient <TResponseMessage>(responseMessageEntityType);

            var messageHandlerFunction = new Func <AzureServiceBusMessage, CancellationToken, Task>(async(azureServiceBusRequestMessage, cancellationToken) =>
            {
                var lockToken = azureServiceBusRequestMessage.SystemProperties?.LockToken;

                if (lockToken is null)
                {
                    throw new MessageSubscriptionException("The lock token is invalid.");
                }

                var deserializedRequestMessage = DeserializeMessage <TRequestMessage>(azureServiceBusRequestMessage.Body);
                var requestMessageIdentifier   = deserializedRequestMessage.Identifier.ToSerializedString();

                if (requestReceiveClient.IsClosedOrClosing)
                {
                    throw new MessageSubscriptionException("The message cannot be processed because the receive client is unavailable.");
                }

                try
                {
                    cancellationToken.ThrowIfCancellationRequested();
                    var responseMessage                = requestMessageHandler(deserializedRequestMessage);
                    var serializedResponseMessage      = SerializeMessage(responseMessage);
                    var azureServiceBusResponseMessage = AzureServiceBusClientManager.CreateAzureServiceBusMessage(serializedResponseMessage, responseMessageType, MessageSerializationFormat, responseMessage.Identifier, responseMessage.CorrelationIdentifier, deserializedRequestMessage.Identifier);

                    await responseSendClient.SendAsync(azureServiceBusResponseMessage).ContinueWith(async(task) =>
                    {
                        switch (task.Status)
                        {
                        case TaskStatus.RanToCompletion:

                            await requestReceiveClient.CompleteAsync(lockToken).ConfigureAwait(false);
                            break;

                        default:

                            await requestReceiveClient.AbandonAsync(lockToken).ConfigureAwait(false);
                            break;
                        }
                    }).ConfigureAwait(false);
                }
                catch
                {
                    await requestReceiveClient.AbandonAsync(lockToken).ConfigureAwait(false);
                    throw;
                }
            });

            requestReceiveClient.RegisterMessageHandler(messageHandlerFunction, ClientManager.ReceiverOptionsForRequestMessages);
        }
        /// <summary>
        /// Asynchronously publishes the specified request message to a bus and waits for the correlated response message.
        /// </summary>
        /// <typeparam name="TRequestMessage">
        /// The type of the request message.
        /// </typeparam>
        /// <typeparam name="TResponseMessage">
        /// The type of the response message.
        /// </typeparam>
        /// <param name="requestMessage">
        /// The request message to publish.
        /// </param>
        /// <param name="controlToken">
        /// A token that ensures thread safety for the operation.
        /// </param>
        /// <returns>
        /// A task representing the asynchronous operation and containing the correlated response message.
        /// </returns>
        protected sealed override async Task <TResponseMessage> RequestAsync <TRequestMessage, TResponseMessage>(TRequestMessage requestMessage, ConcurrencyControlToken controlToken)
        {
            var requestMessageType            = typeof(TRequestMessage);
            var serializedRequestMessage      = SerializeMessage(requestMessage);
            var azureServiceBusRequestMessage = AzureServiceBusClientManager.CreateAzureServiceBusMessage(serializedRequestMessage, requestMessageType, MessageSerializationFormat, requestMessage.Identifier, requestMessage.CorrelationIdentifier);
            var requestClient = ClientManager.GetRequestClient <TRequestMessage, TResponseMessage>();

            ClientManager.AddOutstandingRequest(requestMessage.Identifier);

            using (var sendTask = requestClient.SendAsync(azureServiceBusRequestMessage))
            {
                using (var waitForResponseTask = ClientManager.WaitForResponseAsync(requestMessage.Identifier))
                {
                    await Task.WhenAll(sendTask, waitForResponseTask);

                    return(DeserializeMessage <TResponseMessage>(waitForResponseTask.Result.Body));
                }
            }
        }