< Summary

Class:Azure.Messaging.ServiceBus.ServiceBusSender
Assembly:Azure.Messaging.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Azure.Messaging.ServiceBus\src\Sender\ServiceBusSender.cs
Covered lines:154
Uncovered lines:40
Coverable lines:194
Total lines:565
Line coverage:79.3% (154 of 194)
Covered branches:18
Total branches:26
Branch coverage:69.2% (18 of 26)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
get_FullyQualifiedNamespace()-100%100%
get_EntityPath()-100%100%
get_IsDisposed()-0%100%
get_Logger()-100%100%
get_ViaEntityPath()-100%100%
get_Identifier()-100%100%
.ctor(...)-89.66%62.5%
.ctor()-100%100%
SendMessageAsync()-100%100%
SendMessagesAsync()-100%100%
ApplyPlugins()-7.69%25%
CreateDiagnosticScope(...)-100%100%
InstrumentMessages(...)-90%83.33%
CreateMessageBatchAsync(...)-100%100%
CreateMessageBatchAsync()-78.57%75%
SendMessagesAsync()-78.95%100%
ScheduleMessageAsync()-100%100%
ScheduleMessagesAsync()-100%100%
CancelScheduledMessageAsync()-0%100%
CancelScheduledMessagesAsync()-100%100%
DisposeAsync()-0%100%
Equals(...)-0%100%
GetHashCode()-0%100%
ToString()-0%100%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Azure.Messaging.ServiceBus\src\Sender\ServiceBusSender.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;
 8using System.Diagnostics.CodeAnalysis;
 9using System.Globalization;
 10using System.Linq;
 11using System.Threading;
 12using System.Threading.Tasks;
 13using Azure.Core;
 14using Azure.Core.Pipeline;
 15using Azure.Messaging.ServiceBus.Core;
 16using Azure.Messaging.ServiceBus.Diagnostics;
 17using Azure.Messaging.ServiceBus.Plugins;
 18
 19namespace Azure.Messaging.ServiceBus
 20{
 21    /// <summary>
 22    ///   A client responsible for sending <see cref="ServiceBusMessage" /> to a specific Service Bus entity
 23    ///   (Queue or Topic). It can be used for both session and non-session entities. It is constructed by calling <see 
 24    /// </summary>
 25    ///
 26    public class ServiceBusSender : IAsyncDisposable
 27    {
 28        /// <summary>The minimum allowable size, in bytes, for a batch to be sent.</summary>
 29        internal const int MinimumBatchSizeLimit = 24;
 30
 31        /// <summary>
 32        ///   The fully qualified Service Bus namespace that the producer is associated with.  This is likely
 33        ///   to be similar to <c>{yournamespace}.servicebus.windows.net</c>.
 34        /// </summary>
 35        ///
 3236        public string FullyQualifiedNamespace => _connection.FullyQualifiedNamespace;
 37
 38        /// <summary>
 39        ///   The path of the entity that the sender is connected to, specific to the
 40        ///   Service Bus namespace that contains it.
 41        /// </summary>
 42        ///
 6243        public string EntityPath { get; }
 44
 45        /// <summary>
 46        ///   Indicates whether or not this <see cref="ServiceBusSender"/> has been disposed.
 47        /// </summary>
 48        ///
 49        /// <value>
 50        ///   <c>true</c> if the client is disposed; otherwise, <c>false</c>.
 51        /// </value>
 52        ///
 053        public bool IsDisposed { get; private set; } = false;
 54
 55        /// <summary>
 56        ///   The instance of <see cref="ServiceBusEventSource" /> which can be mocked for testing.
 57        /// </summary>
 58        ///
 16259        internal ServiceBusEventSource Logger { get; set; } = ServiceBusEventSource.Log;
 60
 61        /// <summary>
 62        /// In the case of a via-sender, the message is sent to <see cref="EntityPath"/> via <see cref="ViaEntityPath"/>
 63        /// </summary>
 3064        public string ViaEntityPath { get; }
 65
 66        /// <summary>
 67        /// Gets the ID to identify this client. This can be used to correlate logs and exceptions.
 68        /// </summary>
 69        /// <remarks>Every new client has a unique ID.</remarks>
 20470        internal string Identifier { get; private set; }
 71
 72        /// <summary>
 73        ///   The policy to use for determining retry behavior for when an operation fails.
 74        /// </summary>
 75        ///
 76        private readonly ServiceBusRetryPolicy _retryPolicy;
 77
 78        /// <summary>
 79        ///   The active connection to the Azure Service Bus service, enabling client communications for metadata
 80        ///   about the associated Service Bus entity and access to transport-aware consumers.
 81        /// </summary>
 82        ///
 83        private readonly ServiceBusConnection _connection;
 84
 85        /// <summary>
 86        ///   An abstracted Service Bus entity transport-specific sender that is associated with the
 87        ///   Service Bus entity gateway rather than a specific partition; intended to perform delegated operations.
 88        /// </summary>
 89        ///
 90        private readonly TransportSender _innerSender;
 91        private readonly EntityScopeFactory _scopeFactory;
 92        internal readonly IList<ServiceBusPlugin> _plugins;
 93
 94        /// <summary>
 95        ///   Initializes a new instance of the <see cref="ServiceBusSender"/> class.
 96        /// </summary>
 97        /// <param name="entityPath">The entity path to send the message to.</param>
 98        /// <param name="options">The set of <see cref="ServiceBusSenderOptions"/> to use for configuring
 99        /// this <see cref="ServiceBusSender"/>.</param>
 100        /// <param name="connection">The connection for the sender.</param>
 101        /// <param name="plugins">Plugins to apply to outgoing messages.</param>
 102        ///
 30103        internal ServiceBusSender(
 30104            string entityPath,
 30105            ServiceBusSenderOptions options,
 30106            ServiceBusConnection connection,
 30107            IList<ServiceBusPlugin> plugins)
 108        {
 30109            Logger.ClientCreateStart(typeof(ServiceBusSender), connection?.FullyQualifiedNamespace, entityPath);
 110            try
 111            {
 30112                Argument.AssertNotNull(connection, nameof(connection));
 30113                Argument.AssertNotNull(connection.RetryOptions, nameof(connection.RetryOptions));
 30114                Argument.AssertNotNullOrWhiteSpace(entityPath, nameof(entityPath));
 30115                connection.ThrowIfClosed();
 116
 30117                options = options?.Clone() ?? new ServiceBusSenderOptions();
 30118                EntityPath = entityPath;
 30119                ViaEntityPath = options.ViaQueueOrTopicName;
 30120                Identifier = DiagnosticUtilities.GenerateIdentifier(EntityPath);
 30121                _connection = connection;
 30122                _retryPolicy = _connection.RetryOptions.ToRetryPolicy();
 30123                _innerSender = _connection.CreateTransportSender(
 30124                    entityPath,
 30125                    ViaEntityPath,
 30126                    _retryPolicy,
 30127                    Identifier);
 30128                _scopeFactory = new EntityScopeFactory(EntityPath, FullyQualifiedNamespace);
 30129                _plugins = plugins;
 30130            }
 0131            catch (Exception ex)
 132            {
 0133                Logger.ClientCreateException(typeof(ServiceBusSender), connection?.FullyQualifiedNamespace, entityPath, 
 0134                throw;
 135            }
 30136            Logger.ClientCreateComplete(typeof(ServiceBusSender), Identifier);
 30137        }
 138
 139        /// <summary>
 140        ///   Initializes a new instance of the <see cref="ServiceBusSender"/> class for mocking.
 141        /// </summary>
 142        ///
 16143        protected ServiceBusSender()
 144        {
 16145        }
 146
 147        /// <summary>
 148        ///   Sends a message to the associated entity of Service Bus.
 149        /// </summary>
 150        /// <param name="message"></param>
 151        ///
 152        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 153        ///
 154        /// <returns>A task to be resolved on when the operation has completed.</returns>
 155        ///
 156        public virtual async Task SendMessageAsync(
 157            ServiceBusMessage message,
 158            CancellationToken cancellationToken = default)
 159        {
 8160            Argument.AssertNotNull(message, nameof(message));
 6161            await SendMessagesAsync(
 6162                new ServiceBusMessage[] { message },
 6163                cancellationToken).ConfigureAwait(false);
 4164        }
 165
 166        /// <summary>
 167        ///   Sends a set of messages to the associated Service Bus entity using a batched approach.
 168        ///   If the size of the messages exceed the maximum size of a single batch,
 169        ///   an exception will be triggered and the send will fail. In order to ensure that the messages
 170        ///   being sent will fit in a batch, use <see cref="SendMessagesAsync(ServiceBusMessageBatch, CancellationToken
 171        /// </summary>
 172        ///
 173        /// <param name="messages">The set of messages to send.</param>
 174        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 175        ///
 176        /// <returns>A task to be resolved on when the operation has completed.</returns>
 177        ///
 178        public virtual async Task SendMessagesAsync(
 179            IEnumerable<ServiceBusMessage> messages,
 180            CancellationToken cancellationToken = default)
 181        {
 10182            Argument.AssertNotNull(messages, nameof(messages));
 8183            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusSender));
 8184            IList<ServiceBusMessage> messageList = messages.ToList();
 8185            if (messageList.Count == 0)
 186            {
 2187                return;
 188            }
 6189            await ApplyPlugins(messageList).ConfigureAwait(false);
 6190            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 6191            Logger.SendMessageStart(Identifier, messageCount: messageList.Count);
 6192            using DiagnosticScope scope = CreateDiagnosticScope(messages, DiagnosticProperty.SendActivityName);
 6193            scope.Start();
 194
 195            try
 196            {
 6197                await _innerSender.SendAsync(
 6198                    messageList,
 6199                    cancellationToken).ConfigureAwait(false);
 4200            }
 201
 2202            catch (Exception exception)
 203            {
 2204                Logger.SendMessageException(Identifier, exception.ToString());
 2205                scope.Failed(exception);
 2206                throw;
 207            }
 4208            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4209            Logger.SendMessageComplete(Identifier);
 6210        }
 211
 212        private async Task ApplyPlugins(IList<ServiceBusMessage> messages)
 213        {
 0214            foreach (ServiceBusPlugin plugin in _plugins)
 215            {
 0216                string pluginType = plugin.GetType().Name;
 0217                foreach (ServiceBusMessage message in messages)
 218                {
 219                    try
 220                    {
 0221                        Logger.PluginCallStarted(pluginType, message.MessageId);
 0222                        await plugin.BeforeMessageSendAsync(message).ConfigureAwait(false);
 0223                        Logger.PluginCallCompleted(pluginType, message.MessageId);
 0224                    }
 0225                    catch (Exception ex)
 226                    {
 0227                        Logger.PluginCallException(pluginType, message.MessageId, ex.ToString());
 0228                        throw;
 229                    }
 0230                }
 0231            }
 10232        }
 233
 234        private DiagnosticScope CreateDiagnosticScope(IEnumerable<ServiceBusMessage> messages, string activityName)
 235        {
 14236            InstrumentMessages(messages);
 237
 238            // create a new scope for the specified operation
 14239            DiagnosticScope scope = _scopeFactory.CreateScope(
 14240                activityName,
 14241                DiagnosticProperty.ClientKind);
 242
 14243            scope.SetMessageData(messages);
 14244            return scope;
 245        }
 246
 247        /// <summary>
 248        ///   Performs the actions needed to instrument a set of messages.
 249        /// </summary>
 250        ///
 251        /// <param name="messages">The messages to instrument.</param>
 252        ///
 253        private void InstrumentMessages(IEnumerable<ServiceBusMessage> messages)
 254        {
 64255            foreach (ServiceBusMessage message in messages)
 256            {
 18257                if (!message.Properties.ContainsKey(DiagnosticProperty.DiagnosticIdAttribute))
 258                {
 18259                    using DiagnosticScope messageScope = _scopeFactory.CreateScope(
 18260                        DiagnosticProperty.MessageActivityName,
 18261                        DiagnosticProperty.SenderKind);
 18262                    messageScope.Start();
 263
 18264                    Activity activity = Activity.Current;
 18265                    if (activity != null)
 266                    {
 0267                        message.Properties[DiagnosticProperty.DiagnosticIdAttribute] = activity.Id;
 268                    }
 269                }
 270            }
 14271        }
 272
 273        /// <summary>
 274        ///   Creates a size-constraint batch to which <see cref="ServiceBusMessage" /> may be added using
 275        ///   a <see cref="ServiceBusMessageBatch.TryAddMessage"/>. If a message would exceed the maximum
 276        ///   allowable size of the batch, the batch will not allow adding the message and signal that
 277        ///   scenario using it return value.
 278        ///
 279        ///   Because messages that would violate the size constraint cannot be added, publishing a batch
 280        ///   will not trigger an exception when attempting to send the messages to the Queue/Topic.
 281        /// </summary>
 282        ///
 283        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 284        ///
 285        /// <returns>An <see cref="ServiceBusMessageBatch" /> with the default batch options.</returns>
 286        ///
 287        /// <seealso cref="CreateMessageBatchAsync(CreateMessageBatchOptions, CancellationToken)" />
 288        ///
 2289        public virtual ValueTask<ServiceBusMessageBatch> CreateMessageBatchAsync(CancellationToken cancellationToken = d
 290
 291        /// <summary>
 292        ///   Creates a size-constraint batch to which <see cref="ServiceBusMessage" /> may be added using a try-based p
 293        ///   exceed the maximum allowable size of the batch, the batch will not allow adding the message and signal tha
 294        ///   return value.
 295        ///
 296        ///   Because messages that would violate the size constraint cannot be added, publishing a batch will not trigg
 297        ///   attempting to send the messages to the Queue/Topic.
 298        /// </summary>
 299        ///
 300        /// <param name="options">The set of options to consider when creating this batch.</param>
 301        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 302        ///
 303        /// <returns>An <see cref="ServiceBusMessageBatch" /> with the requested <paramref name="options"/>.</returns>
 304        ///
 305        /// <seealso cref="CreateMessageBatchAsync(CreateMessageBatchOptions, CancellationToken)" />
 306        ///
 307        public virtual async ValueTask<ServiceBusMessageBatch> CreateMessageBatchAsync(
 308            CreateMessageBatchOptions options,
 309            CancellationToken cancellationToken = default)
 310        {
 2311            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusSender));
 2312            options = options?.Clone() ?? new CreateMessageBatchOptions();
 2313            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 2314            Logger.CreateMessageBatchStart(Identifier);
 315            ServiceBusMessageBatch batch;
 316            try
 317            {
 2318                TransportMessageBatch transportBatch = await _innerSender.CreateMessageBatchAsync(options, cancellationT
 2319                batch = new ServiceBusMessageBatch(transportBatch);
 2320            }
 0321            catch (Exception ex)
 322            {
 0323                Logger.CreateMessageBatchException(Identifier, ex.ToString());
 0324                throw;
 325            }
 2326            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 2327            Logger.CreateMessageBatchComplete(Identifier);
 2328            return batch;
 2329        }
 330
 331        /// <summary>
 332        ///   Sends a <see cref="ServiceBusMessageBatch"/>
 333        ///   containing a set of <see cref="ServiceBusMessage"/> to
 334        ///   the associated Service Bus entity.
 335        /// </summary>
 336        ///
 337        /// <param name="messageBatch">The batch of messages to send. A batch may be created using <see cref="CreateMess
 338        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 339        /// <returns>A task to be resolved on when the operation has completed.</returns>
 340        ///
 341        public virtual async Task SendMessagesAsync(
 342            ServiceBusMessageBatch messageBatch,
 343            CancellationToken cancellationToken = default)
 344        {
 6345            Argument.AssertNotNull(messageBatch, nameof(messageBatch));
 4346            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusSender));
 4347            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4348            Logger.SendMessageStart(Identifier, messageBatch.Count);
 4349            using DiagnosticScope scope = CreateDiagnosticScope(
 4350                messageBatch.AsEnumerable<ServiceBusMessage>(),
 4351                DiagnosticProperty.SendActivityName);
 4352            scope.Start();
 353
 354            try
 355            {
 4356                messageBatch.Lock();
 4357                await _innerSender.SendBatchAsync(messageBatch, cancellationToken).ConfigureAwait(false);
 4358            }
 0359            catch (Exception exception)
 360            {
 0361                Logger.SendMessageException(Identifier, exception.ToString());
 0362                scope.Failed(exception);
 0363                throw;
 364            }
 365            finally
 366            {
 4367                messageBatch.Unlock();
 368            }
 369
 4370            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4371            Logger.SendMessageComplete(Identifier);
 4372        }
 373
 374        /// <summary>
 375        /// Schedules a message to appear on Service Bus at a later time.
 376        /// </summary>
 377        ///
 378        /// <param name="message">The <see cref="ServiceBusMessage"/> to schedule.</param>
 379        /// <param name="scheduledEnqueueTime">The UTC time at which the message should be available for processing</par
 380        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 381        ///
 382        /// <remarks>Although the message will not be available to be received until the scheduledEnqueueTime, it can st
 383        /// Messages can also be scheduled by setting <see cref="ServiceBusMessage.ScheduledEnqueueTime"/> and
 384        /// using <see cref="SendMessageAsync(ServiceBusMessage, CancellationToken)"/>,
 385        /// <see cref="SendMessagesAsync(IEnumerable{ServiceBusMessage}, CancellationToken)"/>, or
 386        /// <see cref="SendMessagesAsync(ServiceBusMessageBatch, CancellationToken)"/>.</remarks>
 387        ///
 388        /// <returns>The sequence number of the message that was scheduled.</returns>
 389        public virtual async Task<long> ScheduleMessageAsync(
 390            ServiceBusMessage message,
 391            DateTimeOffset scheduledEnqueueTime,
 392            CancellationToken cancellationToken = default)
 393        {
 6394            Argument.AssertNotNull(message, nameof(message));
 4395            long[] sequenceNumbers = await ScheduleMessagesAsync(
 4396                new ServiceBusMessage[] { message },
 4397                scheduledEnqueueTime,
 4398                cancellationToken)
 4399            .ConfigureAwait(false);
 400            // if there isn't one sequence number in the array, an
 401            // exception should have been thrown by this point.
 2402            return sequenceNumbers[0];
 2403        }
 404
 405
 406
 407        /// <summary>
 408        /// Schedules a set of messages to appear on Service Bus at a later time.
 409        /// </summary>
 410        ///
 411        /// <param name="messages">The set of messages to schedule.</param>
 412        /// <param name="scheduledEnqueueTime">The UTC time at which the message should be available for processing</par
 413        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 414        ///
 415        /// <remarks>Although the message will not be available to be received until the scheduledEnqueueTime, it can st
 416        /// Messages can also be scheduled by setting <see cref="ServiceBusMessage.ScheduledEnqueueTime"/> and
 417        /// using <see cref="SendMessageAsync(ServiceBusMessage, CancellationToken)"/>,
 418        /// <see cref="SendMessagesAsync(IEnumerable{ServiceBusMessage}, CancellationToken)"/>, or
 419        /// <see cref="SendMessagesAsync(ServiceBusMessageBatch, CancellationToken)"/>.</remarks>
 420        ///
 421        /// <returns>The sequence number of the message that was scheduled.</returns>
 422        public virtual async Task<long[]> ScheduleMessagesAsync(
 423            IEnumerable<ServiceBusMessage> messages,
 424            DateTimeOffset scheduledEnqueueTime,
 425            CancellationToken cancellationToken = default)
 426        {
 8427            Argument.AssertNotNullOrEmpty(messages, nameof(messages));
 4428            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusSender));
 4429            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4430            var messageList = messages.ToList();
 4431            await ApplyPlugins(messageList).ConfigureAwait(false);
 4432            Logger.ScheduleMessagesStart(
 4433                Identifier,
 4434                messageList.Count,
 4435                scheduledEnqueueTime.ToString(CultureInfo.InvariantCulture));
 436
 4437            using DiagnosticScope scope = CreateDiagnosticScope(
 4438                messages,
 4439                DiagnosticProperty.ScheduleActivityName);
 4440            scope.Start();
 441
 4442            long[] sequenceNumbers = null;
 443            try
 444            {
 16445                foreach (ServiceBusMessage message in messageList)
 446                {
 4447                    message.ScheduledEnqueueTime = scheduledEnqueueTime.UtcDateTime;
 448                }
 4449                sequenceNumbers = await _innerSender.ScheduleMessagesAsync(messageList, cancellationToken).ConfigureAwai
 2450            }
 2451            catch (Exception exception)
 452            {
 2453                Logger.ScheduleMessagesException(Identifier, exception.ToString());
 2454                scope.Failed(exception);
 2455                throw;
 456            }
 457
 2458            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 2459            Logger.ScheduleMessagesComplete(Identifier);
 2460            scope.AddAttribute(DiagnosticProperty.SequenceNumbersAttribute, sequenceNumbers);
 2461            return sequenceNumbers;
 2462        }
 463
 464        /// <summary>
 465        /// Cancels a message that was scheduled.
 466        /// </summary>
 467        /// <param name="sequenceNumber">The <see cref="ServiceBusReceivedMessage.SequenceNumber"/> of the message to be
 468        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 469        public virtual async Task CancelScheduledMessageAsync(
 470            long sequenceNumber,
 471            CancellationToken cancellationToken = default) =>
 0472            await CancelScheduledMessagesAsync(
 0473                new long[] { sequenceNumber },
 0474                cancellationToken)
 0475            .ConfigureAwait(false);
 476
 477        /// <summary>
 478        /// Cancels a set of messages that were scheduled.
 479        /// </summary>
 480        /// <param name="sequenceNumbers">The set of <see cref="ServiceBusReceivedMessage.SequenceNumber"/> of the messa
 481        /// <param name="cancellationToken">An optional <see cref="CancellationToken"/> instance to signal the request t
 482        public virtual async Task CancelScheduledMessagesAsync(
 483            IEnumerable<long> sequenceNumbers,
 484            CancellationToken cancellationToken = default)
 485        {
 4486            Argument.AssertNotDisposed(IsDisposed, nameof(ServiceBusSender));
 4487            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 4488            var sequenceNumberList = sequenceNumbers.ToArray();
 4489            Logger.CancelScheduledMessagesStart(Identifier, sequenceNumberList);
 4490            using DiagnosticScope scope = _scopeFactory.CreateScope(
 4491                DiagnosticProperty.CancelActivityName,
 4492                DiagnosticProperty.ClientKind);
 493
 4494            scope.AddAttribute(DiagnosticProperty.SequenceNumbersAttribute, sequenceNumbers);
 4495            scope.Start();
 496            try
 497            {
 4498                await _innerSender.CancelScheduledMessagesAsync(sequenceNumberList, cancellationToken).ConfigureAwait(fa
 2499            }
 2500            catch (Exception ex)
 501            {
 2502                Logger.CancelScheduledMessagesException(Identifier, ex.ToString());
 2503                throw;
 504            }
 505
 2506            cancellationToken.ThrowIfCancellationRequested<TaskCanceledException>();
 2507            Logger.CancelScheduledMessagesComplete(Identifier);
 2508        }
 509
 510        /// <summary>
 511        ///   Performs the task needed to clean up resources used by the <see cref="ServiceBusSender" />.
 512        /// </summary>
 513        ///
 514        /// <returns>A task to be resolved on when the operation has completed.</returns>
 515        ///
 516        [SuppressMessage("Usage", "AZC0002:Ensure all service methods take an optional CancellationToken parameter.", Ju
 517        public virtual async ValueTask DisposeAsync()
 518        {
 0519            IsDisposed = true;
 520
 0521            Logger.ClientDisposeStart(typeof(ServiceBusSender), Identifier);
 522
 523            try
 524            {
 0525                await _innerSender.CloseAsync(CancellationToken.None).ConfigureAwait(false);
 0526            }
 0527            catch (Exception ex)
 528            {
 0529                Logger.ClientDisposeException(typeof(ServiceBusSender), Identifier, ex);
 0530                throw;
 531            }
 532
 0533            Logger.ClientDisposeComplete(typeof(ServiceBusSender), Identifier);
 0534        }
 535
 536        /// <summary>
 537        ///   Determines whether the specified <see cref="System.Object" /> is equal to this instance.
 538        /// </summary>
 539        ///
 540        /// <param name="obj">The <see cref="System.Object" /> to compare with this instance.</param>
 541        ///
 542        /// <returns><c>true</c> if the specified <see cref="System.Object" /> is equal to this instance; otherwise, <c>
 543        ///
 544        [EditorBrowsable(EditorBrowsableState.Never)]
 0545        public override bool Equals(object obj) => base.Equals(obj);
 546
 547        /// <summary>
 548        ///   Returns a hash code for this instance.
 549        /// </summary>
 550        ///
 551        /// <returns>A hash code for this instance, suitable for use in hashing algorithms and data structures like a ha
 552        ///
 553        [EditorBrowsable(EditorBrowsableState.Never)]
 0554        public override int GetHashCode() => base.GetHashCode();
 555
 556        /// <summary>
 557        ///   Converts the instance to string representation.
 558        /// </summary>
 559        ///
 560        /// <returns>A <see cref="System.String" /> that represents this instance.</returns>
 561        ///
 562        [EditorBrowsable(EditorBrowsableState.Never)]
 0563        public override string ToString() => base.ToString();
 564    }
 565}