public override async Task OpenAsync(CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, cancellationToken, $"{nameof(OpenAsync)}");
            }

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                await _amqpUnit.OpenAsync(_operationTimeout).ConfigureAwait(false);
            }
            catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException))
            {
                Exception newException = AmqpIoTExceptionAdapter.ConvertToIoTHubException(exception);

                if (newException != exception)
                {
                    throw newException;
                }
                else
                {
                    // Maintain the original stack.
                    throw;
                }
            }
            finally
            {
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, cancellationToken, $"{nameof(OpenAsync)}");
                }
            }
        }
        private async Task DisposeMessageAsync(string lockToken, AmqpIoTDisposeActions outcome, CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, outcome, $"{nameof(DisposeMessageAsync)}");
            }

            AmqpIoTOutcome disposeOutcome;

            try
            {
                // Currently, the same mechanism is used for sending feedback for C2D messages and events received by modules.
                // However, devices only support C2D messages (they cannot receive events), and modules only support receiving events
                // (they cannot receive C2D messages). So we use this to distinguish whether to dispose the message (i.e. send outcome on)
                // the DeviceBoundReceivingLink or the EventsReceivingLink.
                // If this changes (i.e. modules are able to receive C2D messages, or devices are able to receive telemetry), this logic
                // will have to be updated.
                disposeOutcome = await _amqpUnit.DisposeMessageAsync(lockToken, outcome, _operationTimeout).ConfigureAwait(false);
            }
            catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException))
            {
                throw AmqpIoTExceptionAdapter.ConvertToIoTHubException(exception);
            }

            disposeOutcome.ThrowIfError();

            if (Logging.IsEnabled)
            {
                Logging.Exit(this, outcome, $"{nameof(DisposeMessageAsync)}");
            }
        }
        public override async Task <Twin> SendTwinGetAsync(CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, cancellationToken, $"{nameof(SendTwinGetAsync)}");
            }
            try
            {
                await EnableTwinPatchAsync(cancellationToken).ConfigureAwait(false);

                Twin twin = await RoundTripTwinMessage(AmqpTwinMessageType.Get, null, cancellationToken).ConfigureAwait(false);

                if (twin == null)
                {
                    throw new InvalidOperationException("Service rejected the message");
                }
                return(twin);
            }
            catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException))
            {
                throw AmqpIoTExceptionAdapter.ConvertToIoTHubException(exception);
            }
            finally
            {
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, cancellationToken, $"{nameof(SendTwinGetAsync)}");
                }
            }
        }
        public override async Task EnableTwinPatchAsync(CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, cancellationToken, $"{nameof(EnableTwinPatchAsync)}");
            }

            try
            {
                cancellationToken.ThrowIfCancellationRequested();

                await _amqpUnit.EnsureTwinLinksAreOpenedAsync(_operationTimeout).ConfigureAwait(false);
            }
            catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException))
            {
                throw AmqpIoTExceptionAdapter.ConvertToIoTHubException(exception);
            }
            finally
            {
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, cancellationToken, $"{nameof(EnableTwinPatchAsync)}");
                }
            }
        }
        public override async Task SendTwinPatchAsync(TwinCollection reportedProperties, CancellationToken cancellationToken)
        {
            if (Logging.IsEnabled)
            {
                Logging.Enter(this, reportedProperties, cancellationToken, $"{nameof(SendTwinPatchAsync)}");
            }
            try
            {
                await EnableTwinPatchAsync(cancellationToken).ConfigureAwait(false);

                Twin twin = await RoundTripTwinMessage(AmqpTwinMessageType.Patch, reportedProperties, cancellationToken).ConfigureAwait(false);
            }
            catch (Exception exception) when(!exception.IsFatal() && !(exception is OperationCanceledException))
            {
                throw AmqpIoTExceptionAdapter.ConvertToIoTHubException(exception);
            }
            finally
            {
                if (Logging.IsEnabled)
                {
                    Logging.Exit(this, reportedProperties, cancellationToken, $"{nameof(SendTwinPatchAsync)}");
                }
            }
        }