< Summary

Class:Azure.Messaging.ServiceBus.ServiceBusReceiver
Assembly:Azure.Messaging.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Azure.Messaging.ServiceBus\src\Receiver\ServiceBusReceiver.cs
Covered lines:268
Uncovered lines:68
Coverable lines:336
Total lines:1015
Line coverage:79.7% (268 of 336)
Covered branches:18
Total branches:48
Branch coverage:37.5% (18 of 48)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_FullyQualifiedNamespace()-100%100%
get_EntityPath()-100%100%
get_ReceiveMode()-100%100%
get_IsSessionReceiver()-100%100%
get_PrefetchCount()-100%100%
get_Identifier()-100%100%
get_IsDisposed()-100%100%
get_InnerReceiver()-100%100%
get_ScopeFactory()-100%100%
get_Logger()-100%100%
.ctor(...)-92.11%50%
.ctor()-100%100%
ReceiveMessagesAsync()-100%100%
ReceiveMessagesAsync()-0%0%
ReceiveMessageAsync()-88.89%50%
ApplyPlugins()-7.69%25%
PeekMessageAsync()-88.89%50%
PeekMessagesAsync()-100%100%
PeekMessagesInternalAsync()-100%100%
OpenLinkAsync()-0%100%
CompleteMessageAsync()-100%100%
CompleteMessageAsync()-100%100%
AbandonMessageAsync()-100%100%
AbandonMessageAsync()-100%100%
DeadLetterMessageAsync()-100%100%
DeadLetterMessageAsync()-100%100%
DeadLetterMessageAsync()-0%100%
DeadLetterMessageAsync()-0%100%
DeadLetterInternalAsync()-100%100%
DeferMessageAsync()-100%100%
DeferMessageAsync()-100%100%
ThrowIfNotPeekLockMode()-66.67%50%
ThrowIfLockTokenIsEmpty(...)-100%100%
ReceiveDeferredMessageAsync()-0%100%
ReceiveDeferredMessagesAsync()-0%100%
RenewMessageLockAsync()-100%100%
RenewMessageLockAsync()-100%100%
ThrowIfSessionReceiver()-66.67%50%
DisposeAsync()-70%100%
Equals(...)-0%100%
GetHashCode()-0%100%
ToString()-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Azure.Messaging.ServiceBus\src\Receiver\ServiceBusReceiver.cs

#LineLine coverage
 1// Copyright (c) Microsoft Corporation. All rights reserved.
 2// Licensed under the MIT License.
 3
 4using System;
 5using System.Collections.Generic;
 6using System.ComponentModel;
 7using System.Diagnostics.CodeAnalysis;
 8using System.Linq;
 9using System.Runtime.CompilerServices;
 10using System.Threading;
 11using System.Threading.Tasks;
 12using Azure.Core;
 13using Azure.Core.Pipeline;
 14using Azure.Messaging.ServiceBus.Core;
 15using Azure.Messaging.ServiceBus.Diagnostics;
 16using Azure.Messaging.ServiceBus.Plugins;
 17
 18namespace Azure.Messaging.ServiceBus
 19{
 20    /// <summary>
 21    /// The <see cref="ServiceBusReceiver" /> is responsible for receiving
 22    /// <see cref="ServiceBusReceivedMessage" /> and settling messages from Queues and Subscriptions.
 23    /// It is constructed by calling <see cref="ServiceBusClient.CreateReceiver(string, ServiceBusReceiverOptions)"/>.
 24    /// </summary>
 25    public class ServiceBusReceiver : IAsyncDisposable
 26    {
 27        /// <summary>
 28        /// The fully qualified Service Bus namespace that the receiver is associated with.  This is likely
 29        /// to be similar to <c>{yournamespace}.servicebus.windows.net</c>.
 30        /// </summary>
 6031        public string FullyQualifiedNamespace => _connection.FullyQualifiedNamespace;
 32
 33        /// <summary>
 34        /// The path of the Service Bus entity that the receiver is connected to, specific to the
 35        /// Service Bus namespace that contains it.
 36        /// </summary>
 11837        public string EntityPath { get; }
 38
 39        /// <summary>
 40        /// The <see cref="ReceiveMode"/> used to specify how messages are received. Defaults to PeekLock mode.
 41        /// </summary>
 8842        public ReceiveMode ReceiveMode { get; }
 43
 44        /// <summary>
 45        /// Indicates whether the receiver entity is session enabled.
 46        /// </summary>
 6847        internal bool IsSessionReceiver { get; }
 48
 49        /// <summary>
 50        /// The number of messages that will be eagerly requested from Queues or Subscriptions and queued locally withou
 51        /// whether a processing is currently active, intended to help maximize throughput by allowing the receiver to r
 52        /// from a local cache rather than waiting on a service request.
 53        /// </summary>
 8054        public int PrefetchCount { get; }
 55
 56        /// <summary>
 57        /// Gets the ID to identify this client. This can be used to correlate logs and exceptions.
 58        /// </summary>
 59        /// <remarks>Every new client has a unique ID.</remarks>
 41860        internal string Identifier { get; }
 61
 62        /// <summary>
 63        ///   Indicates whether or not this <see cref="ServiceBusReceiver"/> has been disposed.
 64        /// </summary>
 65        ///
 66        /// <value>
 67        /// <c>true</c> if the client is disposed; otherwise, <c>false</c>.
 68        /// </value>
 6069        public bool IsDisposed { get; private set; } = false;
 70
 71        /// <summary>
 72        /// The policy to use for determining retry behavior for when an operation fails.
 73        /// </summary>
 74        ///
 75        private readonly ServiceBusRetryPolicy _retryPolicy;
 76
 77        /// <summary>
 78        /// The active connection to the Azure Service Bus service, enabling client communications for metadata
 79        /// about the associated Service Bus entity and access to transport-aware receivers.
 80        /// </summary>
 81        ///
 82        private readonly ServiceBusConnection _connection;
 83
 84        /// <summary>
 85        /// An abstracted Service Bus transport-specific receiver that is associated with the
 86        /// Service Bus entity gateway; intended to perform delegated operations.
 87        /// </summary>
 8088        internal TransportReceiver InnerReceiver => _innerReceiver;
 89        private readonly TransportReceiver _innerReceiver;
 90
 91        /// <summary>
 92        /// Responsible for creating entity scopes.
 93        /// </summary>
 5294        internal EntityScopeFactory ScopeFactory => _scopeFactory;
 95        private readonly EntityScopeFactory _scopeFactory;
 96
 97        /// <summary>
 98        /// The list of plugins to apply to incoming messages.
 99        /// </summary>
 100        private readonly IList<ServiceBusPlugin> _plugins;
 101
 102        /// <summary>
 103        ///   The instance of <see cref="ServiceBusEventSource" /> which can be mocked for testing.
 104        /// </summary>
 105        ///
 330106        internal ServiceBusEventSource Logger { get; set; } = ServiceBusEventSource.Log;
 107
 108        /// <summary>
 109        /// Initializes a new instance of the <see cref="ServiceBusReceiver"/> class.
 110        /// </summary>
 111        ///
 112        /// <param name="connection">The <see cref="ServiceBusConnection" /> connection to use for communication with th
 113        /// <param name="entityPath"></param>
 114        /// <param name="isSessionEntity"></param>
 115        /// <param name="plugins">The plugins to apply to incoming messages.</param>
 116        /// <param name="options">A set of options to apply when configuring the consumer.</param>
 117        /// <param name="sessionId">An optional session Id to scope the receiver to. If not specified,
 118        /// the next available session returned from the service will be used.</param>
 119        ///
 58120        internal ServiceBusReceiver(
 58121            ServiceBusConnection connection,
 58122            string entityPath,
 58123            bool isSessionEntity,
 58124            IList<ServiceBusPlugin> plugins,
 58125            ServiceBusReceiverOptions options,
 58126            string sessionId = default)
 127        {
 58128            Type type = GetType();
 58129            Logger.ClientCreateStart(type, connection?.FullyQualifiedNamespace, entityPath);
 130            try
 131            {
 58132                Argument.AssertNotNull(connection, nameof(connection));
 58133                Argument.AssertNotNull(connection.RetryOptions, nameof(connection.RetryOptions));
 58134                Argument.AssertNotNullOrWhiteSpace(entityPath, nameof(entityPath));
 58135                connection.ThrowIfClosed();
 58136                options = options?.Clone() ?? new ServiceBusReceiverOptions();
 58137                Identifier = DiagnosticUtilities.GenerateIdentifier(entityPath);
 58138                _connection = connection;
 58139                _retryPolicy = connection.RetryOptions.ToRetryPolicy();
 58140                ReceiveMode = options.ReceiveMode;
 58141                PrefetchCount = options.PrefetchCount;
 58142                EntityPath = entityPath;
 58143                IsSessionReceiver = isSessionEntity;
 58144                _innerReceiver = _connection.CreateTransportReceiver(
 58145                    entityPath: EntityPath,
 58146                    retryPolicy: _retryPolicy,
 58147                    receiveMode: ReceiveMode,
 58148                    prefetchCount: (uint)PrefetchCount,
 58149                    identifier: Identifier,
 58150                    sessionId: sessionId,
 58151                    isSessionReceiver: IsSessionReceiver);
 58152                _scopeFactory = new EntityScopeFactory(EntityPath, FullyQualifiedNamespace);
 58153                _plugins = plugins;
 58154                if (!isSessionEntity)
 155                {
 156                    // don't log client completion for session receiver here as it is not complete until
 157                    // the link is opened.
 44158                    Logger.ClientCreateComplete(type, Identifier);
 159                }
 58160            }
 0161            catch (Exception ex)
 162            {
 0163                Logger.ClientCreateException(type, connection?.FullyQualifiedNamespace, entityPath, ex);
 0164                throw;
 165            }
 58166        }
 167
 168        /// <summary>
 169        /// Initializes a new instance of the <see cref="ServiceBusReceiver"/> class for mocking.
 170        /// </summary>
 171        ///
 16172        protected ServiceBusReceiver() { }
 173
 174        /// <summary>
 175        /// Receives a list of <see cref="ServiceBusReceivedMessage" /> from the entity using <see cref="ReceiveMode"/> 
 176        /// <see cref="ReceiveMode"/> defaults to PeekLock mode.
 177        /// This method doesn't guarantee to return exact `maxMessages` messages,
 178        /// even if there are `maxMessages` messages available in the queue or topic.
 179        /// </summary>
 180        ///
 181        /// <param name="maxMessages">The maximum number of messages that will be received.</param>
 182        /// <param name="maxWaitTime">An optional <see cref="TimeSpan"/> specifying the maximum time to wait for the fir
 183        /// If not specified, the <see cref="ServiceBusRetryOptions.TryTimeout"/> will be used.</param>
 184        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 185        ///
 186        /// <returns>List of messages received. Returns an empty list if no message is found.</returns>
 187        public virtual async Task<IReadOnlyList<ServiceBusReceivedMessage>> ReceiveMessagesAsync(
 188           int maxMessages,
 189           TimeSpan? maxWaitTime = default,
 190           CancellationToken cancellationToken = default)
 191        {
 14192            Argument.AssertAtLeast(maxMessages, 1, nameof(maxMessages));
 14193            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 14194            if (maxWaitTime.HasValue)
 195            {
 4196                Argument.AssertPositive(maxWaitTime.Value, nameof(maxWaitTime));
 197            }
 10198            if (PrefetchCount > 0 && maxMessages > PrefetchCount)
 199            {
 2200                Logger.MaxMessagesExceedsPrefetch(Identifier, PrefetchCount, maxMessages);
 201            }
 202
 10203            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 10204            Logger.ReceiveMessageStart(Identifier, maxMessages);
 10205            using DiagnosticScope scope = ScopeFactory.CreateScope(
 10206                DiagnosticProperty.ReceiveActivityName,
 10207                requestedMessageCount: maxMessages);
 10208            scope.Start();
 209
 10210            IReadOnlyList<ServiceBusReceivedMessage> messages = null;
 211
 212            try
 213            {
 10214                messages = await InnerReceiver.ReceiveMessagesAsync(
 10215                    maxMessages,
 10216                    maxWaitTime,
 10217                    cancellationToken).ConfigureAwait(false);
 8218                await ApplyPlugins(messages).ConfigureAwait(false);
 8219            }
 2220            catch (Exception exception)
 221            {
 2222                Logger.ReceiveMessageException(Identifier, exception.ToString());
 2223                scope.Failed(exception);
 2224                throw;
 225            }
 226
 8227            Logger.ReceiveMessageComplete(Identifier, messages.Count);
 8228            scope.SetMessageData(messages);
 229
 8230            return messages;
 8231        }
 232
 233        /// <summary>
 234        /// Receives messages as an asynchronous enumerable from the entity using <see cref="ReceiveMode"/> mode.
 235        /// <see cref="ReceiveMode"/> defaults to PeekLock mode. Messages will be received from the entity as
 236        /// the IAsyncEnumerable is iterated. If no messages are available, this method will continue polling
 237        /// until messages are available, i.e. it will never return null.
 238        /// </summary>
 239        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the
 240        /// request to cancel the operation.</param>
 241        /// <returns>The message received.</returns>
 242        public virtual async IAsyncEnumerable<ServiceBusReceivedMessage> ReceiveMessagesAsync(
 243            [EnumeratorCancellation]
 244            CancellationToken cancellationToken = default)
 245        {
 0246            while (!cancellationToken.IsCancellationRequested)
 247            {
 0248                var msg = await ReceiveMessageAsync(cancellationToken: cancellationToken).ConfigureAwait(false);
 0249                if (msg == null)
 250                {
 251                    continue;
 252                }
 0253                yield return msg;
 254            }
 255
 256            // Surface the TCE to ensure deterministic behavior when cancelling.
 0257            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 0258        }
 259
 260        /// <summary>
 261        /// Receives a <see cref="ServiceBusReceivedMessage" /> from the entity using <see cref="ReceiveMode"/> mode.
 262        /// <see cref="ReceiveMode"/> defaults to PeekLock mode.
 263        /// </summary>
 264        /// <param name="maxWaitTime">An optional <see cref="TimeSpan"/> specifying the maximum time to wait for a messa
 265        /// null if no messages are available.
 266        /// If not specified, the <see cref="ServiceBusRetryOptions.TryTimeout"/> will be used.</param>
 267        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 268        /// operation.</param>
 269        ///
 270        /// <returns>The message received. Returns null if no message is found.</returns>
 271        public virtual async Task<ServiceBusReceivedMessage> ReceiveMessageAsync(
 272            TimeSpan? maxWaitTime = default,
 273            CancellationToken cancellationToken = default)
 274        {
 12275            IEnumerable<ServiceBusReceivedMessage> result = await ReceiveMessagesAsync(
 12276                maxMessages: 1,
 12277                maxWaitTime: maxWaitTime,
 12278                cancellationToken: cancellationToken)
 12279                .ConfigureAwait(false);
 280
 18281            foreach (ServiceBusReceivedMessage message in result)
 282            {
 6283                return message;
 284            }
 0285            return null;
 6286        }
 287
 288        private async Task ApplyPlugins(IReadOnlyList<ServiceBusReceivedMessage> messages)
 289        {
 0290            foreach (ServiceBusPlugin plugin in _plugins)
 291            {
 0292                string pluginType = plugin.GetType().Name;
 0293                foreach (ServiceBusReceivedMessage message in messages)
 294                {
 295                    try
 296                    {
 0297                        Logger.PluginCallStarted(pluginType, message.MessageId);
 0298                        await plugin.AfterMessageReceiveAsync(message).ConfigureAwait(false);
 0299                        Logger.PluginCallCompleted(pluginType, message.MessageId);
 0300                    }
 0301                    catch (Exception ex)
 302                    {
 0303                        Logger.PluginCallException(pluginType, message.MessageId, ex.ToString());
 0304                        throw;
 305                    }
 0306                }
 0307            }
 8308        }
 309
 310        /// <summary>
 311        /// Fetches the next active <see cref="ServiceBusReceivedMessage"/> without changing the state of the receiver o
 312        /// </summary>
 313        /// <param name="fromSequenceNumber">An optional sequence number from where to peek the
 314        /// message. This corresponds to the <see cref="ServiceBusReceivedMessage.SequenceNumber"/>.</param>
 315        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 316        /// operation.</param>
 317        ///
 318        /// <remarks>
 319        /// The first call to <see cref="PeekMessageAsync(long?, CancellationToken)"/> fetches the first active message 
 320        /// Unlike a received message, a peeked message will not have a lock token associated with it, and hence it cann
 321        /// Also, unlike <see cref="ReceiveMessageAsync(TimeSpan?, CancellationToken)"/>, this method will fetch even De
 322        /// </remarks>
 323        ///
 324        /// <returns>The <see cref="ServiceBusReceivedMessage" /> that represents the next message to be read. Returns n
 325        public virtual async Task<ServiceBusReceivedMessage> PeekMessageAsync(
 326            long? fromSequenceNumber = default,
 327            CancellationToken cancellationToken = default)
 328        {
 6329            IEnumerable<ServiceBusReceivedMessage> result = await PeekMessagesInternalAsync(
 6330                sequenceNumber: fromSequenceNumber,
 6331                maxMessages: 1,
 6332                cancellationToken: cancellationToken)
 6333                .ConfigureAwait(false);
 334
 12335            foreach (ServiceBusReceivedMessage message in result)
 336            {
 4337                return message;
 338            }
 0339            return null;
 4340        }
 341
 342        /// Fetches a list of active messages without changing the state of the receiver or the message source.
 343        ///
 344        /// <param name="maxMessages">The maximum number of messages that will be fetched.</param>
 345        /// <param name="fromSequenceNumber">An optional sequence number from where to peek the
 346        /// message. This corresponds to the <see cref="ServiceBusReceivedMessage.SequenceNumber"/>.</param>
 347        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 348        /// the operation.</param>
 349        ///
 350        /// <remarks>
 351        /// Unlike a received message, a peeked message will not have a lock token associated with it, and hence it cann
 352        /// Completed/Abandoned/Deferred/Deadlettered/Renewed.
 353        /// Also, unlike <see cref="ReceiveMessageAsync(TimeSpan?, CancellationToken)"/>, this method will fetch even De
 354        /// </remarks>
 355        ///
 356        /// <returns>An <see cref="IReadOnlyList{ServiceBusReceivedMessage}" /> of messages that were peeked.</returns>
 357        public virtual async Task<IReadOnlyList<ServiceBusReceivedMessage>> PeekMessagesAsync(
 358            int maxMessages,
 359            long? fromSequenceNumber = default,
 360            CancellationToken cancellationToken = default) =>
 4361            await PeekMessagesInternalAsync(
 4362                sequenceNumber: fromSequenceNumber,
 4363                maxMessages: maxMessages,
 4364                cancellationToken: cancellationToken).ConfigureAwait(false);
 365
 366        /// <summary>
 367        /// Fetches a list of active messages without changing the state of the receiver or the message source.
 368        /// </summary>
 369        /// <param name="sequenceNumber">The sequence number from where to peek the message.</param>
 370        /// <param name="maxMessages">The maximum number of messages that will be fetched.</param>
 371        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 372        /// <returns>An <see cref="IList{ServiceBusReceivedMessage}" /> of messages that were peeked.</returns>
 373        private async Task<IReadOnlyList<ServiceBusReceivedMessage>> PeekMessagesInternalAsync(
 374            long? sequenceNumber,
 375            int maxMessages,
 376            CancellationToken cancellationToken)
 377        {
 10378            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 10379            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 10380            Logger.PeekMessageStart(Identifier, sequenceNumber, maxMessages);
 10381            using DiagnosticScope scope = ScopeFactory.CreateScope(
 10382                DiagnosticProperty.PeekActivityName,
 10383                requestedMessageCount: maxMessages);
 10384            scope.Start();
 385
 10386            IReadOnlyList<ServiceBusReceivedMessage> messages = new List<ServiceBusReceivedMessage>();
 387            try
 388            {
 10389                messages = await InnerReceiver.PeekMessagesAsync(
 10390                    sequenceNumber,
 10391                    maxMessages,
 10392                    cancellationToken)
 10393                    .ConfigureAwait(false);
 8394            }
 2395            catch (Exception exception)
 396            {
 2397                Logger.PeekMessageException(Identifier, exception.ToString());
 2398                scope.Failed(exception);
 2399                throw;
 400            }
 401
 8402            Logger.PeekMessageComplete(Identifier, messages.Count);
 8403            scope.SetMessageData(messages);
 8404            return messages;
 8405        }
 406
 407        /// <summary>
 408        /// Opens an AMQP link for use with receiver operations.
 409        /// </summary>
 410        ///
 411        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 412        ///
 413        /// <returns>A task to be resolved on when the operation has completed.</returns>
 414        internal async Task OpenLinkAsync(CancellationToken cancellationToken) =>
 0415            await InnerReceiver.OpenLinkAsync(cancellationToken).ConfigureAwait(false);
 416
 417        /// <summary>
 418        /// Completes a <see cref="ServiceBusReceivedMessage"/>. This will delete the message from the service.
 419        /// </summary>
 420        /// <param name="message">The message to complete.</param>
 421        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 422        ///
 423        /// <remarks>
 424        /// This operation can only be performed on a message that was received by this receiver
 425        /// when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 426        /// </remarks>
 427        ///
 428        /// <returns>A task to be resolved on when the operation has completed.</returns>
 429        public virtual async Task CompleteMessageAsync(
 430            ServiceBusReceivedMessage message,
 431            CancellationToken cancellationToken = default)
 432        {
 4433            Argument.AssertNotNull(message, nameof(message));
 4434            await CompleteMessageAsync(message.LockToken, cancellationToken).ConfigureAwait(false);
 2435        }
 436
 437        /// <summary>
 438        /// Completes a <see cref="ServiceBusReceivedMessage"/>. This will delete the message from the service.
 439        /// </summary>
 440        ///
 441        /// <param name="lockToken">The lock token of the <see cref="ServiceBusReceivedMessage"/> message to complete.</
 442        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 443        ///
 444        /// <remarks>
 445        /// This operation can only be performed on a message that was received by this receiver
 446        /// when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 447        /// </remarks>
 448        ///
 449        /// <returns>A task to be resolved on when the operation has completed.</returns>
 450        public virtual async Task CompleteMessageAsync(
 451            string lockToken,
 452            CancellationToken cancellationToken = default)
 453        {
 4454            ThrowIfLockTokenIsEmpty(lockToken);
 4455            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 4456            Argument.AssertNotNullOrEmpty(lockToken, nameof(lockToken));
 4457            ThrowIfNotPeekLockMode();
 4458            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4459            Logger.CompleteMessageStart(
 4460                Identifier,
 4461                1,
 4462                lockToken);
 4463            using DiagnosticScope scope = ScopeFactory.CreateScope(
 4464                DiagnosticProperty.CompleteActivityName,
 4465                lockToken: lockToken);
 4466            scope.Start();
 467
 468            try
 469            {
 4470                await InnerReceiver.CompleteAsync(
 4471                    lockToken,
 4472                    cancellationToken).ConfigureAwait(false);
 2473            }
 2474            catch (Exception exception)
 475            {
 2476                Logger.CompleteMessageException(Identifier, exception.ToString());
 2477                scope.Failed(exception);
 2478                throw;
 479            }
 480
 2481            Logger.CompleteMessageComplete(Identifier);
 2482        }
 483
 484        /// <summary>
 485        /// Abandons a <see cref="ServiceBusReceivedMessage"/>.This will make the message available again for immediate 
 486        /// </summary>
 487        ///
 488        /// <param name="message">The <see cref="ServiceBusReceivedMessage"/> to abandon.</param>
 489        /// <param name="propertiesToModify">The properties of the message to modify while abandoning the message.</para
 490        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 491        ///
 492        /// <remarks>
 493        /// Abandoning a message will increase the delivery count on the message.
 494        /// This operation can only be performed on messages that were received by this receiver
 495        /// when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 496        /// </remarks>
 497        ///
 498        /// <returns>A task to be resolved on when the operation has completed.</returns>
 499        public virtual async Task AbandonMessageAsync(
 500            ServiceBusReceivedMessage message,
 501            IDictionary<string, object> propertiesToModify = null,
 502            CancellationToken cancellationToken = default)
 503        {
 4504            Argument.AssertNotNull(message, nameof(message));
 4505            await AbandonMessageAsync(
 4506                message.LockToken,
 4507                propertiesToModify,
 4508                cancellationToken).ConfigureAwait(false);
 2509        }
 510
 511        /// <summary>
 512        /// Abandons a <see cref="ServiceBusReceivedMessage"/>. This will make the message available again for processin
 513        /// </summary>
 514        ///
 515        /// <param name="lockToken">The lock token <see cref="ServiceBusReceivedMessage"/> to abandon.</param>
 516        /// <param name="propertiesToModify">The properties of the message to modify while abandoning the message.</para
 517        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 518        ///
 519        /// <remarks>
 520        /// Abandoning a message will increase the delivery count on the message.
 521        /// This operation can only be performed on messages that were received by this receiver
 522        /// when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 523        /// </remarks>
 524        ///
 525        /// <returns>A task to be resolved on when the operation has completed.</returns>
 526        public virtual async Task AbandonMessageAsync(
 527            string lockToken,
 528            IDictionary<string, object> propertiesToModify = null,
 529            CancellationToken cancellationToken = default)
 530        {
 4531            ThrowIfLockTokenIsEmpty(lockToken);
 4532            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 4533            ThrowIfNotPeekLockMode();
 4534            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4535            Logger.AbandonMessageStart(Identifier, 1, lockToken);
 4536            using DiagnosticScope scope = ScopeFactory.CreateScope(
 4537                DiagnosticProperty.AbandonActivityName,
 4538                lockToken: lockToken);
 4539            scope.Start();
 540
 541            try
 542            {
 4543                await InnerReceiver.AbandonAsync(
 4544                    lockToken,
 4545                    propertiesToModify,
 4546                    cancellationToken).ConfigureAwait(false);
 2547            }
 2548            catch (Exception exception)
 549            {
 2550                Logger.AbandonMessageException(Identifier, exception.ToString());
 2551                scope.Failed(exception);
 2552                throw;
 553            }
 554
 2555            Logger.AbandonMessageComplete(Identifier);
 2556        }
 557
 558        /// <summary>
 559        /// Moves a message to the deadletter sub-queue.
 560        /// </summary>
 561        ///
 562        /// <param name="message">The <see cref="ServiceBusReceivedMessage"/> to deadletter.</param>
 563        /// <param name="propertiesToModify">The properties of the message to modify while moving to sub-queue.</param>
 564        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 565        ///
 566        /// <remarks>
 567        /// In order to receive a message from the deadletter queue, you can call
 568        /// <see cref="ServiceBusClient.CreateDeadLetterReceiver(string, ServiceBusReceiverOptions)"/>
 569        /// or <see cref="ServiceBusClient.CreateDeadLetterReceiver(string, string, ServiceBusReceiverOptions)"/>
 570        /// to create a receiver for the queue or subscription.
 571        /// This operation can only be performed when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLoc
 572        /// </remarks>
 573        public virtual async Task DeadLetterMessageAsync(
 574            ServiceBusReceivedMessage message,
 575            IDictionary<string, object> propertiesToModify = null,
 576            CancellationToken cancellationToken = default)
 577        {
 4578            Argument.AssertNotNull(message, nameof(message));
 4579            await DeadLetterMessageAsync(
 4580                lockToken: message.LockToken,
 4581                propertiesToModify: propertiesToModify,
 4582                cancellationToken: cancellationToken).ConfigureAwait(false);
 2583        }
 584
 585        /// <summary>
 586        /// Moves a message to the deadletter sub-queue.
 587        /// </summary>
 588        ///
 589        /// <param name="lockToken">The lock token of the <see cref="ServiceBusReceivedMessage"/> to deadletter.</param>
 590        /// <param name="propertiesToModify">The properties of the message to modify while moving to sub-queue.</param>
 591        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 592        ///
 593        /// <remarks>
 594        /// In order to receive a message from the deadletter queue, you can call
 595        /// <see cref="ServiceBusClient.CreateDeadLetterReceiver(string, ServiceBusReceiverOptions)"/>
 596        /// or <see cref="ServiceBusClient.CreateDeadLetterReceiver(string, string, ServiceBusReceiverOptions)"/>
 597        /// to create a receiver for the queue or subscription.
 598        /// This operation can only be performed when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLoc
 599        /// </remarks>
 600        public virtual async Task DeadLetterMessageAsync(
 601            string lockToken,
 602            IDictionary<string, object> propertiesToModify = null,
 603            CancellationToken cancellationToken = default) =>
 4604            await DeadLetterInternalAsync(
 4605                lockToken: lockToken,
 4606                propertiesToModify: propertiesToModify,
 4607                cancellationToken: cancellationToken).ConfigureAwait(false);
 608
 609        /// <summary>
 610        /// Moves a message to the deadletter sub-queue.
 611        /// </summary>
 612        ///
 613        /// <param name="message">The <see cref="ServiceBusReceivedMessage"/> to deadletter.</param>
 614        /// <param name="deadLetterReason">The reason for deadlettering the message.</param>
 615        /// <param name="deadLetterErrorDescription">The error description for deadlettering the message.</param>
 616        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 617        ///
 618        /// <remarks>
 619        /// A lock token can be found in <see cref="ServiceBusReceivedMessage.LockToken"/>,
 620        /// only when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 621        /// In order to receive a message from the deadletter queue, you will need a new <see cref="ServiceBusReceiver"/
 622        /// You can use EntityNameHelper.FormatDeadLetterPath(string) to help with this.
 623        /// This operation can only be performed on messages that were received by this receiver.
 624        /// </remarks>
 625        public virtual async Task DeadLetterMessageAsync(
 626            ServiceBusReceivedMessage message,
 627            string deadLetterReason,
 628            string deadLetterErrorDescription = default,
 629            CancellationToken cancellationToken = default)
 630        {
 0631            Argument.AssertNotNull(message, nameof(message));
 0632            await DeadLetterMessageAsync(
 0633                lockToken: message.LockToken,
 0634                deadLetterReason: deadLetterReason,
 0635                deadLetterErrorDescription: deadLetterErrorDescription,
 0636                cancellationToken: cancellationToken).ConfigureAwait(false);
 0637        }
 638
 639        /// <summary>
 640        /// Moves a message to the deadletter sub-queue.
 641        /// </summary>
 642        ///
 643        /// <param name="lockToken">The lock token of the <see cref="ServiceBusReceivedMessage"/> to deadletter.</param>
 644        /// <param name="deadLetterReason">The reason for deadlettering the message.</param>
 645        /// <param name="deadLetterErrorDescription">The error description for deadlettering the message.</param>
 646        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 647        ///
 648        /// <remarks>
 649        /// A lock token can be found in <see cref="ServiceBusReceivedMessage.LockToken"/>,
 650        /// only when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 651        /// In order to receive a message from the deadletter queue, you will need a new <see cref="ServiceBusReceiver"/
 652        /// You can use EntityNameHelper.FormatDeadLetterPath(string) to help with this.
 653        /// This operation can only be performed on messages that were received by this receiver.
 654        /// </remarks>
 655        public virtual async Task DeadLetterMessageAsync(
 656            string lockToken,
 657            string deadLetterReason,
 658            string deadLetterErrorDescription = null,
 659            CancellationToken cancellationToken = default) =>
 0660            await DeadLetterInternalAsync(
 0661                lockToken: lockToken,
 0662                deadLetterReason: deadLetterReason,
 0663                deadLetterErrorDescription: deadLetterErrorDescription,
 0664                cancellationToken: cancellationToken).ConfigureAwait(false);
 665
 666        /// <summary>
 667        /// Moves a message to the deadletter sub-queue.
 668        /// </summary>
 669        ///
 670        /// <param name="lockToken">The lock token <see cref="ServiceBusReceivedMessage"/> to deadletter.</param>
 671        /// <param name="deadLetterReason">The reason for deadlettering the message.</param>
 672        /// <param name="deadLetterErrorDescription">The error description for deadlettering the message.</param>
 673        /// <param name="propertiesToModify">The properties of the message to modify while deferring the message.</param
 674        /// <param name="cancellationToken"></param>
 675        ///
 676        /// <remarks>
 677        /// A lock token can be found in <see cref="ServiceBusReceivedMessage.LockToken"/>,
 678        /// only when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 679        /// In order to receive a message from the deadletter queue, you will need a new <see cref="ServiceBusReceiver"/
 680        /// You can use EntityNameHelper.FormatDeadLetterPath(string) to help with this.
 681        /// This operation can only be performed on messages that were received by this receiver.
 682        /// </remarks>
 683        private async Task DeadLetterInternalAsync(
 684            string lockToken,
 685            string deadLetterReason = default,
 686            string deadLetterErrorDescription = default,
 687            IDictionary<string, object> propertiesToModify = default,
 688            CancellationToken cancellationToken = default)
 689        {
 4690            ThrowIfLockTokenIsEmpty(lockToken);
 4691            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 4692            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4693            ThrowIfNotPeekLockMode();
 4694            Logger.DeadLetterMessageStart(Identifier, 1, lockToken);
 4695            using DiagnosticScope scope = ScopeFactory.CreateScope(
 4696                DiagnosticProperty.DeadLetterActivityName,
 4697                lockToken: lockToken);
 4698            scope.Start();
 699
 700            try
 701            {
 4702                await InnerReceiver.DeadLetterAsync(
 4703                    lockToken: lockToken,
 4704                    deadLetterReason: deadLetterReason,
 4705                    deadLetterErrorDescription: deadLetterErrorDescription,
 4706                    propertiesToModify: propertiesToModify,
 4707                    cancellationToken: cancellationToken).ConfigureAwait(false);
 2708            }
 2709            catch (Exception exception)
 710            {
 2711                Logger.DeadLetterMessageException(Identifier, exception.ToString());
 2712                scope.Failed(exception);
 2713                throw;
 714            }
 715
 2716            Logger.DeadLetterMessageComplete(Identifier);
 2717        }
 718
 719        /// <summary> Indicates that the receiver wants to defer the processing for the message.</summary>
 720        ///
 721        /// <param name="message">The <see cref="ServiceBusReceivedMessage"/> to defer.</param>
 722        /// <param name="propertiesToModify">The properties of the message to modify while deferring the message.</param
 723        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 724        ///
 725        /// <remarks>
 726        /// A lock token can be found in <see cref="ServiceBusReceivedMessage.LockToken"/>,
 727        /// only when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 728        /// In order to receive this message again in the future, you will need to save the
 729        /// <see cref="ServiceBusReceivedMessage.SequenceNumber"/>
 730        /// and receive it using <see cref="ReceiveDeferredMessageAsync(long, CancellationToken)"/>.
 731        /// Deferring messages does not impact message's expiration, meaning that deferred messages can still expire.
 732        /// This operation can only be performed on messages that were received by this receiver.
 733        /// </remarks>
 734        ///
 735        /// <returns>A task to be resolved on when the operation has completed.</returns>
 736        public virtual async Task DeferMessageAsync(
 737            ServiceBusReceivedMessage message,
 738            IDictionary<string, object> propertiesToModify = null,
 739            CancellationToken cancellationToken = default)
 740        {
 4741            Argument.AssertNotNull(message, nameof(message));
 4742            await DeferMessageAsync(
 4743                message.LockToken,
 4744                propertiesToModify,
 4745                cancellationToken).ConfigureAwait(false);
 2746        }
 747
 748        /// <summary> Indicates that the receiver wants to defer the processing for the message.</summary>
 749        ///
 750        /// <param name="lockToken">The lockToken of the <see cref="ServiceBusReceivedMessage"/> to defer.</param>
 751        /// <param name="propertiesToModify">The properties of the message to modify while deferring the message.</param
 752        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 753        ///
 754        /// <remarks>
 755        /// A lock token can be found in <see cref="ServiceBusReceivedMessage.LockToken"/>,
 756        /// only when <see cref="ReceiveMode"/> is set to <see cref="ReceiveMode.PeekLock"/>.
 757        /// In order to receive this message again in the future, you will need to save the
 758        /// <see cref="ServiceBusReceivedMessage.SequenceNumber"/>
 759        /// and receive it using <see cref="ReceiveDeferredMessageAsync(long, CancellationToken)"/>.
 760        /// Deferring messages does not impact message's expiration, meaning that deferred messages can still expire.
 761        /// This operation can only be performed on messages that were received by this receiver.
 762        /// </remarks>
 763        ///
 764        /// <returns>A task to be resolved on when the operation has completed.</returns>
 765        public virtual async Task DeferMessageAsync(
 766            string lockToken,
 767            IDictionary<string, object> propertiesToModify = null,
 768            CancellationToken cancellationToken = default)
 769        {
 4770            ThrowIfLockTokenIsEmpty(lockToken);
 4771            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 4772            ThrowIfNotPeekLockMode();
 4773            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4774            Logger.DeferMessageStart(Identifier, 1, lockToken);
 4775            using DiagnosticScope scope = ScopeFactory.CreateScope(
 4776                DiagnosticProperty.DeferActivityName,
 4777                lockToken: lockToken);
 4778            scope.Start();
 779
 780            try
 781            {
 4782                await InnerReceiver.DeferAsync(
 4783                    lockToken,
 4784                    propertiesToModify,
 4785                    cancellationToken).ConfigureAwait(false);
 2786            }
 2787            catch (Exception exception)
 788            {
 2789                Logger.DeferMessageException(Identifier, exception.ToString());
 2790                scope.Failed(exception);
 2791                throw;
 792            }
 793
 2794            Logger.DeferMessageComplete(Identifier);
 2795        }
 796
 797        /// <summary>
 798        /// Throws an InvalidOperationException when not in PeekLock mode.
 799        /// </summary>
 800        private void ThrowIfNotPeekLockMode()
 801        {
 20802            if (ReceiveMode != ReceiveMode.PeekLock)
 803            {
 0804                throw new InvalidOperationException(Resources.OperationNotSupported);
 805            }
 20806        }
 807
 808        /// <summary>
 809        /// Throws an InvalidOperationException when the lock token is empty.
 810        /// </summary>
 811        private static void ThrowIfLockTokenIsEmpty(string lockToken)
 812        {
 22813            if (Guid.Parse(lockToken) == Guid.Empty)
 814            {
 2815                throw new InvalidOperationException(Resources.SettlementOperationNotSupported);
 816            }
 20817        }
 818
 819        /// <summary>
 820        /// Receives a deferred message identified by <paramref name="sequenceNumber"/>.
 821        /// </summary>
 822        ///
 823        /// <param name="sequenceNumber">The sequence number of the message to receive. This corresponds to
 824        /// the <see cref="ServiceBusReceivedMessage.SequenceNumber"/>.</param>
 825        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 826        ///
 827        /// <returns>The deferred message identified by the specified sequence number. Returns null if no message is fou
 828        /// Throws if the message has not been deferred.</returns>
 829        /// <seealso cref="DeferMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, CancellationToken)"
 830        /// <seealso cref="DeferMessageAsync(string, IDictionary{string, object}, CancellationToken)"/>
 831        public virtual async Task<ServiceBusReceivedMessage> ReceiveDeferredMessageAsync(
 832            long sequenceNumber,
 833            CancellationToken cancellationToken = default) =>
 0834            (await ReceiveDeferredMessagesAsync(new long[] { sequenceNumber }, cancellationToken).ConfigureAwait(false))
 835
 836        /// <summary>
 837        /// Receives a <see cref="IList{ServiceBusReceivedMessage}"/> of deferred messages identified by <paramref name=
 838        /// </summary>
 839        ///
 840        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 841        /// <param name="sequenceNumbers">An <see cref="IEnumerable{T}"/> containing the sequence numbers to receive.</p
 842        ///
 843        /// <returns>Messages identified by sequence number are returned. Returns null if no messages are found.
 844        /// Throws if the messages have not been deferred.</returns>
 845        /// <seealso cref="DeferMessageAsync(ServiceBusReceivedMessage, IDictionary{string, object}, CancellationToken)"
 846        /// <seealso cref="DeferMessageAsync(string, IDictionary{string, object}, CancellationToken)"/>
 847        public virtual async Task<IReadOnlyList<ServiceBusReceivedMessage>> ReceiveDeferredMessagesAsync(
 848            IEnumerable<long> sequenceNumbers,
 849            CancellationToken cancellationToken = default)
 850        {
 0851            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 0852            Argument.AssertNotNullOrEmpty(sequenceNumbers, nameof(sequenceNumbers));
 0853            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 0854            var sequenceNumbersList = sequenceNumbers.ToList();
 855
 0856            Logger.ReceiveDeferredMessageStart(Identifier, sequenceNumbersList);
 0857            using DiagnosticScope scope = ScopeFactory.CreateScope(DiagnosticProperty.ReceiveDeferredActivityName);
 0858            scope.AddAttribute(
 0859                DiagnosticProperty.SequenceNumbersAttribute,
 0860                string.Join(",", sequenceNumbers));
 0861            scope.Start();
 862
 0863            IReadOnlyList<ServiceBusReceivedMessage> deferredMessages = null;
 864            try
 865            {
 0866                deferredMessages = await InnerReceiver.ReceiveDeferredMessagesAsync(
 0867                    sequenceNumbersList,
 0868                    cancellationToken).ConfigureAwait(false);
 0869            }
 0870            catch (Exception exception)
 871            {
 0872                Logger.ReceiveDeferredMessageException(Identifier, exception.ToString());
 0873                scope.Failed(exception);
 0874                throw;
 875            }
 876
 0877            Logger.ReceiveDeferredMessageComplete(Identifier, deferredMessages.Count);
 0878            scope.SetMessageData(deferredMessages);
 0879            return deferredMessages;
 0880        }
 881
 882        /// <summary>
 883        /// Renews the lock on the message. The lock will be renewed based on the setting specified on the queue.
 884        /// </summary>
 885        ///
 886        /// <remarks>
 887        /// When a message is received in <see cref="ReceiveMode.PeekLock"/> mode, the message is locked on the server f
 888        /// receiver instance for a duration as specified during the Queue/Subscription creation (LockDuration).
 889        /// If processing of the message requires longer than this duration, the lock needs to be renewed.
 890        /// For each renewal, it resets the time the message is locked by the LockDuration set on the Entity.
 891        /// </remarks>
 892        ///
 893        /// <param name="message">The <see cref="ServiceBusReceivedMessage"/> to renew the lock for.</param>
 894        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 895        public virtual async Task RenewMessageLockAsync(
 896            ServiceBusReceivedMessage message,
 897            CancellationToken cancellationToken = default)
 898        {
 6899            Argument.AssertNotNull(message, nameof(message));
 6900            DateTimeOffset lockedUntil = await RenewMessageLockAsync(
 6901                message.LockToken,
 6902                cancellationToken).ConfigureAwait(false);
 2903            message.LockedUntil = lockedUntil;
 2904        }
 905
 906        /// <summary>
 907        /// Renews the lock on the message. The lock will be renewed based on the setting specified on the queue.
 908        /// </summary>
 909        ///
 910        /// <remarks>
 911        /// When a message is received in <see cref="ReceiveMode.PeekLock"/> mode, the message is locked on the server f
 912        /// receiver instance for a duration as specified during the Queue/Subscription creation (LockDuration).
 913        /// If processing of the message requires longer than this duration, the lock needs to be renewed.
 914        /// For each renewal, it resets the time the message is locked by the LockDuration set on the Entity.
 915        /// </remarks>
 916        ///
 917        /// <param name="lockToken">The lockToken of the <see cref="ServiceBusReceivedMessage"/> to renew the lock for.<
 918        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 919        public virtual async Task<DateTimeOffset> RenewMessageLockAsync(
 920            string lockToken,
 921            CancellationToken cancellationToken = default)
 922        {
 6923            ThrowIfLockTokenIsEmpty(lockToken);
 4924            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusReceiver));
 4925            ThrowIfNotPeekLockMode();
 4926            ThrowIfSessionReceiver();
 4927            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4928            Logger.RenewMessageLockStart(Identifier, 1, lockToken);
 4929            using DiagnosticScope scope = ScopeFactory.CreateScope(
 4930                DiagnosticProperty.RenewMessageLockActivityName,
 4931                lockToken: lockToken);
 4932            scope.Start();
 933
 934            DateTimeOffset lockedUntil;
 935            try
 936            {
 4937                lockedUntil = await InnerReceiver.RenewMessageLockAsync(
 4938                    lockToken,
 4939                    cancellationToken).ConfigureAwait(false);
 2940            }
 2941            catch (Exception exception)
 942            {
 2943                Logger.RenewMessageLockException(Identifier, exception.ToString());
 2944                scope.Failed(exception);
 2945                throw;
 946            }
 947
 2948            Logger.RenewMessageLockComplete(Identifier);
 2949            scope.AddAttribute(DiagnosticProperty.LockedUntilAttribute, lockedUntil);
 2950            return lockedUntil;
 2951        }
 952
 953        /// <summary>
 954        /// Throws an exception if the receiver instance is a session receiver.
 955        /// </summary>
 956        private void ThrowIfSessionReceiver()
 957        {
 4958            if (IsSessionReceiver)
 959            {
 0960                throw new InvalidOperationException(Resources.CannotLockMessageOnSessionEntity);
 961            }
 4962        }
 963
 964        /// <summary>
 965        /// Performs the task needed to clean up resources used by the <see cref="ServiceBusReceiver" />.
 966        /// </summary>
 967        ///
 968        /// <returns>A task to be resolved on when the operation has completed.</returns>
 969        [SuppressMessage("Usage", "AZC0002:Ensure all service methods take an optional CancellationToken parameter.", Ju
 970        public virtual async ValueTask DisposeAsync()
 971        {
 4972            IsDisposed = true;
 4973            Type clientType = GetType();
 974
 4975            Logger.ClientDisposeStart(clientType, Identifier);
 976            try
 977            {
 4978                await InnerReceiver.CloseAsync(CancellationToken.None).ConfigureAwait(false);
 4979            }
 0980            catch (Exception ex)
 981            {
 0982                Logger.ClientDisposeException(clientType, Identifier, ex);
 0983                throw;
 984            }
 985
 4986            Logger.ClientDisposeComplete(clientType, Identifier);
 4987        }
 988
 989        /// <summary>
 990        /// Determines whether the specified <see cref="System.Object" /> is equal to this instance.
 991        /// </summary>
 992        ///
 993        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
 994        ///
 995        /// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>
 996        [EditorBrowsable(EditorBrowsableState.Never)]
 0997        public override bool Equals(object obj) => base.Equals(obj);
 998
 999        /// <summary>
 1000        /// Returns a hash code for this instance.
 1001        /// </summary>
 1002        ///
 1003        /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a ha
 1004        [EditorBrowsable(EditorBrowsableState.Never)]
 01005        public override int GetHashCode() => base.GetHashCode();
 1006
 1007        /// <summary>
 1008        /// Converts the instance to string representation.
 1009        /// </summary>
 1010        ///
 1011        /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
 1012        [EditorBrowsable(EditorBrowsableState.Never)]
 01013        public override string ToString() => base.ToString();
 1014    }
 1015}