< Summary

Class:Microsoft.Azure.ServiceBus.Message
Assembly:Microsoft.Azure.ServiceBus
File(s):C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Message.cs
Covered lines:69
Uncovered lines:33
Coverable lines:102
Total lines:527
Line coverage:67.6% (69 of 102)
Covered branches:17
Total branches:26
Branch coverage:65.3% (17 of 26)

Metrics

MethodCyclomatic complexity Line coverage Branch coverage
.cctor()-0%100%
.ctor()-100%100%
.ctor(...)-100%100%
get_Body()-100%100%
get_MessageId()-100%100%
set_MessageId(...)-100%100%
get_PartitionKey()-100%100%
set_PartitionKey(...)-100%100%
get_ViaPartitionKey()-100%100%
set_ViaPartitionKey(...)-100%100%
get_SessionId()-100%100%
set_SessionId(...)-100%100%
get_ReplyToSessionId()-100%100%
set_ReplyToSessionId(...)-100%100%
get_ExpiresAtUtc()-0%0%
get_TimeToLive()-100%100%
set_TimeToLive(...)-100%100%
get_CorrelationId()-100%100%
get_Label()-100%100%
get_To()-100%100%
get_ContentType()-100%100%
get_ReplyTo()-100%100%
get_ScheduledEnqueueTimeUtc()-0%100%
get_Size()-0%0%
get_UserProperties()-100%100%
get_SystemProperties()-100%100%
ToString()-100%100%
Clone()-100%100%
ValidateMessageId(...)-75%50%
ValidateSessionId(...)-66.67%75%
ValidatePartitionKey(...)-66.67%75%
.ctor()-100%100%
get_IsLockTokenSet()-0%100%
get_LockToken()-0%100%
get_IsReceived()-100%100%
get_DeliveryCount()-100%100%
set_DeliveryCount(...)-100%100%
get_LockedUntilUtc()-0%100%
set_LockedUntilUtc(...)-0%100%
get_SequenceNumber()-0%100%
set_SequenceNumber(...)-100%100%
get_DeadLetterSource()-0%100%
set_DeadLetterSource(...)-0%100%
get_PartitionId()-0%100%
set_PartitionId(...)-0%100%
get_EnqueuedSequenceNumber()-0%100%
set_EnqueuedSequenceNumber(...)-0%100%
get_EnqueuedTimeUtc()-0%100%
set_EnqueuedTimeUtc(...)-0%100%
get_LockTokenGuid()-0%100%
set_LockTokenGuid(...)-0%100%
get_BodyObject()-100%100%
set_BodyObject(...)-100%100%
ThrowIfNotReceived()-66.67%50%

File(s)

C:\Git\azure-sdk-for-net\sdk\servicebus\Microsoft.Azure.ServiceBus\src\Message.cs

#LineLine coverage
 1// Copyright (c) Microsoft. All rights reserved.
 2// Licensed under the MIT license. See LICENSE file in the project root for full license information.
 3
 4namespace Microsoft.Azure.ServiceBus
 5{
 6    using System;
 7    using System.Collections.Generic;
 8    using System.Globalization;
 9    using Primitives;
 10
 11    /// <summary>
 12    /// The message object used to communicate and transfer data with Service Bus.
 13    /// </summary>
 14    /// <remarks>
 15    /// The message structure is discussed in detail in the <a href="https://docs.microsoft.com/azure/service-bus-messag
 16    /// </remarks>
 17    public class Message
 18    {
 19        /// <summary>
 20        /// User property key representing deadletter reason, when a message is received from a deadletter subqueue of a
 21        /// </summary>
 022        public static string DeadLetterReasonHeader = "DeadLetterReason";
 23
 24        /// <summary>
 25        /// User property key representing detailed error description, when a message is received from a deadletter subq
 26        /// </summary>
 027        public static string DeadLetterErrorDescriptionHeader = "DeadLetterErrorDescription";
 28
 29        private string messageId;
 30        private string sessionId;
 31        private string replyToSessionId;
 32        private string partitionKey;
 33        private string viaPartitionKey;
 34        private TimeSpan timeToLive;
 35
 36        /// <summary>
 37        /// Creates a new Message
 38        /// </summary>
 39        public Message()
 3840            : this(null)
 41        {
 3842        }
 43
 44        /// <summary>
 45        /// Creates a new message from the specified payload.
 46        /// </summary>
 47        /// <param name="body">The payload of the message in bytes</param>
 5448        public Message(byte[] body)
 49        {
 5450            this.Body = body;
 5451            this.SystemProperties = new SystemPropertiesCollection();
 5452            this.UserProperties = new Dictionary<string, object>();
 5453        }
 54
 55        /// <summary>
 56        /// Gets or sets the body of the message.
 57        /// </summary>
 58        /// <remarks>
 59        /// The easiest way to create a new message from a string is the following:
 60        /// <code>
 61        /// message.Body = System.Text.Encoding.UTF8.GetBytes("Message1");
 62        /// </code>
 63        /// </remarks>
 10264        public byte[] Body { get; set; }
 65
 66        /// <summary>
 67        /// Gets or sets the MessageId to identify the message.
 68        /// </summary>
 69        /// <remarks>
 70        ///    The message identifier is an application-defined value that uniquely identifies the
 71        ///    message and its payload. The identifier is a free-form string and can reflect a GUID
 72        ///    or an identifier derived from the application context. If enabled, the
 73        ///    <a href="https://docs.microsoft.com/azure/service-bus-messaging/duplicate-detection">duplicate detection<
 74        ///    feature identifies and removes second and further submissions of messages with the
 75        ///    same MessageId.
 76        /// </remarks>
 77        public string MessageId
 78        {
 1479            get => this.messageId;
 80
 81            set
 82            {
 1083                Message.ValidateMessageId(value);
 1084                this.messageId = value;
 1085            }
 86        }
 87
 88        /// <summary>Gets or sets a partition key for sending a message to a partitioned entity.</summary>
 89        /// <value>The partition key. Maximum length is 128 characters.</value>
 90        /// <remarks>
 91        ///    For <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-partitioning">partitioned
 92        ///    setting this value enables assigning related messages to the same internal partition, so that submission 
 93        ///    order is correctly recorded. The partition is chosen by a hash function over this value and cannot be cho
 94        ///    directly. For session-aware entities, the <see cref="SessionId"/> property overrides this value.
 95        /// </remarks>
 96        public string PartitionKey
 97        {
 1098            get => this.partitionKey;
 99
 100            set
 101            {
 14102                if (this.sessionId != null && this.sessionId != value)
 103                {
 104                    // SessionId is set. Then partition key must be same as session id.
 2105                    throw new InvalidOperationException($"PartitionKey:{value} is not same as SessionId:{this.sessionId}
 106                }
 107
 12108                Message.ValidatePartitionKey(nameof(this.PartitionKey), value);
 12109                this.partitionKey = value;
 12110            }
 111        }
 112
 113        /// <summary>Gets or sets a partition key for sending a message into an entity via a partitioned transfer queue.
 114        /// <value>The partition key. Maximum length is 128 characters. </value>
 115        /// <remarks>
 116        ///    If a message is sent via a transfer queue in the scope of a transaction, this value selects the
 117        ///    transfer queue partition: This is functionally equivalent to <see cref="PartitionKey"/> and ensures that
 118        ///    messages are kept together and in order as they are transferred.
 119        ///    See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-transactions#transfers-an
 120        /// </remarks>
 121        public string ViaPartitionKey
 122        {
 10123            get => this.viaPartitionKey;
 124
 125            set
 126            {
 6127                Message.ValidatePartitionKey(nameof(this.ViaPartitionKey), value);
 6128                this.viaPartitionKey = value;
 6129            }
 130        }
 131
 132        /// <summary>Gets or sets the session identifier for a session-aware entity.</summary>
 133        /// <value>The session identifier. Maximum length is 128 characters.</value>
 134        /// <remarks>
 135        ///    For session-aware entities, this application-defined value specifies the session
 136        ///    affiliation of the message. Messages with the same session identifier are subject
 137        ///    to summary locking and enable exact in-order processing and demultiplexing.
 138        ///    For session-unaware entities, this value is ignored.
 139        ///    See <a href="https://docs.microsoft.com/azure/service-bus-messaging/message-sessions">Message Sessions</a
 140        /// </remarks>
 141        public string SessionId
 142        {
 8143            get => this.sessionId;
 144
 145            set
 146            {
 8147                Message.ValidateSessionId(nameof(this.SessionId), value);
 8148                this.sessionId = value;
 8149                this.PartitionKey = value;
 8150            }
 151        }
 152
 153        /// <summary>Gets or sets a session identifier augmenting the <see cref="ReplyTo"/> address.</summary>
 154        /// <value>Session identifier. Maximum length is 128 characters.</value>
 155        /// <remarks>
 156        ///    This value augments the ReplyTo information and specifies which SessionId should be set
 157        ///    for the reply when sent to the reply entity. See <a href="https://docs.microsoft.com/azure/service-bus-me
 158        /// </remarks>
 159        public string ReplyToSessionId
 160        {
 8161            get => this.replyToSessionId;
 162
 163            set
 164            {
 6165                Message.ValidateSessionId(nameof(this.ReplyToSessionId), value);
 6166                this.replyToSessionId = value;
 6167            }
 168        }
 169
 170        /// <summary>Gets the date and time in UTC at which the message is set to expire.</summary>
 171        /// <value>The message expiration time in UTC. This property is read-only.</value>
 172        /// <exception cref="System.InvalidOperationException">If the message has not been received. For example if a ne
 173        /// <remarks>
 174        ///  The UTC instant at which the message is marked for removal and no longer available for retrieval
 175        ///  from the entity due to expiration. Expiry is controlled by the <see cref="TimeToLive"/> property
 176        ///  and this property is computed from <see cref="SystemPropertiesCollection.EnqueuedTimeUtc"/>+<see cref="Time
 177        public DateTime ExpiresAtUtc
 178        {
 179            get
 180            {
 0181                if (this.TimeToLive >= DateTime.MaxValue.Subtract(this.SystemProperties.EnqueuedTimeUtc))
 182                {
 0183                    return DateTime.MaxValue;
 184                }
 185
 0186                return this.SystemProperties.EnqueuedTimeUtc.Add(this.TimeToLive);
 187            }
 188        }
 189
 190        /// <summary>
 191        /// Gets or sets the message’s "time to live" value.
 192        /// </summary>
 193        /// <value>The message’s time to live value.</value>
 194        /// <remarks>
 195        ///     This value is the relative duration after which the message expires, starting from the instant
 196        ///      the message has been accepted and stored by the broker, as captured in <see cref="SystemPropertiesColle
 197        ///      When not set explicitly, the assumed value is the DefaultTimeToLive for the respective queue or topic.
 198        ///      A message-level <see cref="TimeToLive"/> value cannot be longer than the entity's DefaultTimeToLive
 199        ///      setting and it is silently adjusted if it does.
 200        ///      See <a href="https://docs.microsoft.com/azure/service-bus-messaging/message-expiration">Expiration</a>
 201        /// </remarks>
 202        public TimeSpan TimeToLive
 203        {
 204            get
 205            {
 14206                if (this.timeToLive == TimeSpan.Zero)
 207                {
 2208                    return TimeSpan.MaxValue;
 209                }
 210
 12211                return this.timeToLive;
 212            }
 213
 214            set
 215            {
 6216                TimeoutHelper.ThrowIfNonPositiveArgument(value);
 6217                this.timeToLive = value;
 6218            }
 219        }
 220
 221        /// <summary>Gets or sets the a correlation identifier.</summary>
 222        /// <value>Correlation identifier.</value>
 223        /// <remarks>
 224        ///    Allows an application to specify a context for the message for the purposes of correlation,
 225        ///    for example reflecting the MessageId of a message that is being replied to.
 226        ///    See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messages-payloads?#messag
 227        /// </remarks>
 14228        public string CorrelationId { get; set; }
 229
 230        /// <summary>Gets or sets an application specific label.</summary>
 231        /// <value>The application specific label</value>
 232        /// <remarks>
 233        ///   This property enables the application to indicate the purpose of the message to the receiver in a standard
 234        ///   fashion, similar to an email subject line. The mapped AMQP property is "subject".
 235        /// </remarks>
 14236        public string Label { get; set; }
 237
 238        /// <summary>Gets or sets the "to" address.</summary>
 239        /// <value>The "to" address.</value>
 240        /// <remarks>
 241        ///    This property is reserved for future use in routing scenarios and presently ignored by the broker itself.
 242        ///     Applications can use this value in rule-driven
 243        ///     <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-auto-forwarding">auto-forwar
 244        ///     intended logical destination of the message.
 245        /// </remarks>
 14246        public string To { get; set; }
 247
 248        /// <summary>Gets or sets the content type descriptor.</summary>
 249        /// <value>RFC2045 Content-Type descriptor.</value>
 250        /// <remarks>
 251        ///   Optionally describes the payload of the message, with a descriptor following the format of
 252        ///   RFC2045, Section 5, for example "application/json".
 253        /// </remarks>
 14254        public string ContentType { get; set; }
 255
 256        /// <summary>Gets or sets the address of an entity to send replies to.</summary>
 257        /// <value>The reply entity address.</value>
 258        /// <remarks>
 259        ///    This optional and application-defined value is a standard way to express a reply path
 260        ///    to the receiver of the message. When a sender expects a reply, it sets the value to the
 261        ///    absolute or relative path of the queue or topic it expects the reply to be sent to.
 262        ///    See <a href="https://docs.microsoft.com/azure/service-bus-messaging/service-bus-messages-payloads?#messag
 263        /// </remarks>
 14264        public string ReplyTo { get; set; }
 265
 266        /// <summary>Gets or sets the date and time in UTC at which the message will be enqueued. This
 267        /// property returns the time in UTC; when setting the property, the supplied DateTime value must also be in UTC
 268        /// <value>The scheduled enqueue time in UTC. This value is for delayed message sending.
 269        /// It is utilized to delay messages sending to a specific time in the future.</value>
 270        /// <remarks> Message enqueuing time does not mean that the message will be sent at the same time. It will get e
 271        /// depends on the queue's workload and its state.</remarks>
 0272        public DateTime ScheduledEnqueueTimeUtc { get; set; }
 273
 274        // TODO: Calculate the size of the properties and body
 275        /// <summary>
 276        /// Gets the total size of the message body in bytes.
 277        /// </summary>
 0278        public long Size => this.Body != null ? this.Body.Length : 0;
 279
 280        /// <summary>
 281        /// Gets the "user properties" bag, which can be used for custom message metadata.
 282        /// </summary>
 283        /// <remarks>
 284        /// Only following value types are supported:
 285        /// byte, sbyte, char, short, ushort, int, uint, long, ulong, float, double, decimal,
 286        /// bool, Guid, string, Uri, DateTime, DateTimeOffset, TimeSpan
 287        /// </remarks>
 148288        public IDictionary<string, object> UserProperties { get; internal set; }
 289
 290        /// <summary>
 291        /// Gets the <see cref="SystemPropertiesCollection"/>, which is used to store properties that are set by the sys
 292        /// </summary>
 90293        public SystemPropertiesCollection SystemProperties { get; internal set; }
 294
 295        /// <summary>Returns a string that represents the current message.</summary>
 296        /// <returns>The string representation of the current message.</returns>
 297        public override string ToString()
 298        {
 6299            return string.Format(CultureInfo.CurrentCulture, "{{MessageId:{0}}}", this.MessageId);
 300        }
 301
 302        /// <summary>Clones a message, so that it is possible to send a clone of an already received
 303        /// message as a new message. The system properties of original message
 304        /// are not copied.</summary>
 305        /// <returns>A cloned <see cref="Message" />.</returns>
 306        public Message Clone()
 307        {
 2308            var clone = (Message)this.MemberwiseClone();
 2309            clone.SystemProperties = new SystemPropertiesCollection();
 310
 2311            if (this.Body != null)
 312            {
 2313                var clonedBody = new byte[this.Body.Length];
 2314                Array.Copy(this.Body, clonedBody, this.Body.Length);
 2315                clone.Body = clonedBody;
 316            }
 2317            return clone;
 318        }
 319
 320        private static void ValidateMessageId(string messageId)
 321        {
 10322            if (string.IsNullOrEmpty(messageId) ||
 10323                messageId.Length > Constants.MaxMessageIdLength)
 324            {
 325                // TODO: throw FxTrace.Exception.Argument("messageId", SRClient.MessageIdIsNullOrEmptyOrOverMaxValue(Con
 0326                throw new ArgumentException("MessageIdIsNullOrEmptyOrOverMaxValue");
 327            }
 10328        }
 329
 330        private static void ValidateSessionId(string sessionIdPropertyName, string sessionId)
 331        {
 14332            if (sessionId != null && sessionId.Length > Constants.MaxSessionIdLength)
 333            {
 334                // TODO: throw FxTrace.Exception.Argument("sessionId", SRClient.SessionIdIsOverMaxValue(Constants.MaxSes
 0335                throw new ArgumentException("SessionIdIsOverMaxValue");
 336            }
 14337        }
 338
 339        private static void ValidatePartitionKey(string partitionKeyPropertyName, string partitionKey)
 340        {
 18341            if (partitionKey != null && partitionKey.Length > Constants.MaxPartitionKeyLength)
 342            {
 343                // TODO: throw FxTrace.Exception.Argument(partitionKeyPropertyName, SRClient.PropertyOverMaxValue(partit
 0344                throw new ArgumentException("PropertyValueOverMaxValue");
 345            }
 18346        }
 347
 348        /// <summary>
 349        /// A collection used to store properties which are set by the Service Bus service.
 350        /// </summary>
 351        public sealed class SystemPropertiesCollection
 352        {
 353            int deliveryCount;
 354            DateTime lockedUntilUtc;
 56355            long sequenceNumber = -1;
 356            short partitionId;
 357            long enqueuedSequenceNumber;
 358            DateTime enqueuedTimeUtc;
 359            Guid lockTokenGuid;
 360            string deadLetterSource;
 361
 362            /// <summary>
 363            /// Specifies whether or not there is a lock token set on the current message.
 364            /// </summary>
 365            /// <remarks>A lock token will only be specified if the message was received using <see cref="ReceiveMode.Pe
 0366            public bool IsLockTokenSet => this.lockTokenGuid != default;
 367
 368            /// <summary>
 369            /// Gets the lock token for the current message.
 370            /// </summary>
 371            /// <remarks>
 372            ///   The lock token is a reference to the lock that is being held by the broker in <see cref="ReceiveMode.P
 373            ///   Locks are used to explicitly settle messages as explained in the <a href="https://docs.microsoft.com/a
 374            ///   The token can also be used to pin the lock permanently through the <a href="https://docs.microsoft.com
 375            ///   regular delivery state flow. This property is read-only.
 376            /// </remarks>
 0377            public string LockToken => this.LockTokenGuid.ToString();
 378
 379            /// <summary>Specifies if the message has been obtained from the broker.</summary>
 6380            public bool IsReceived => this.sequenceNumber > -1;
 381
 382            /// <summary>
 383            /// Get the current delivery count.
 384            /// </summary>
 385            /// <value>This value starts at 1.</value>
 386            /// <remarks>
 387            ///    Number of deliveries that have been attempted for this message. The count is incremented when a messa
 388            ///    or the message is explicitly abandoned by the receiver. This property is read-only.
 389            /// </remarks>
 390            public int DeliveryCount
 391            {
 392                get
 393                {
 4394                    this.ThrowIfNotReceived();
 4395                    return this.deliveryCount;
 396                }
 397
 4398                internal set => this.deliveryCount = value;
 399            }
 400
 401            /// <summary>Gets the date and time in UTC until which the message will be locked in the queue/subscription.
 402            /// <value>The date and time until which the message will be locked in the queue/subscription.</value>
 403            /// <remarks>
 404            ///   For messages retrieved under a lock (peek-lock receive mode, not pre-settled) this property reflects t
 405            ///     instant until which the message is held locked in the queue/subscription. When the lock expires, the
 406            ///     is incremented and the message is again available for retrieval. This property is read-only.
 407            /// </remarks>
 408            public DateTime LockedUntilUtc
 409            {
 410                get
 411                {
 0412                    this.ThrowIfNotReceived();
 0413                    return this.lockedUntilUtc;
 414                }
 415
 0416                internal set => this.lockedUntilUtc = value;
 417            }
 418
 419            /// <summary>Gets the unique number assigned to a message by Service Bus.</summary>
 420            /// <remarks>
 421            ///     The sequence number is a unique 64-bit integer assigned to a message as it is accepted
 422            ///     and stored by the broker and functions as its true identifier. For partitioned entities,
 423            ///     the topmost 16 bits reflect the partition identifier. Sequence numbers monotonically increase.
 424            ///     They roll over to 0 when the 48-64 bit range is exhausted. This property is read-only.
 425            /// </remarks>
 426            public long SequenceNumber
 427            {
 428                get
 429                {
 0430                    this.ThrowIfNotReceived();
 0431                    return this.sequenceNumber;
 432                }
 433
 4434                internal set => this.sequenceNumber = value;
 435            }
 436
 437            /// <summary>
 438            /// Gets the name of the queue or subscription that this message was enqueued on, before it was deadlettered
 439            /// </summary>
 440            /// <remarks>
 441            ///   Only set in messages that have been dead-lettered and subsequently auto-forwarded from the dead-letter
 442            ///     to another entity. Indicates the entity in which the message was dead-lettered. This property is rea
 443            /// </remarks>
 444            public string DeadLetterSource
 445            {
 446                get
 447                {
 0448                    this.ThrowIfNotReceived();
 0449                    return this.deadLetterSource;
 450                }
 451
 0452                internal set => this.deadLetterSource = value;
 453            }
 454
 455            internal short PartitionId
 456            {
 457                get
 458                {
 0459                    this.ThrowIfNotReceived();
 0460                    return this.partitionId;
 461                }
 462
 0463                set => this.partitionId = value;
 464            }
 465
 466            /// <summary>Gets or sets the original sequence number of the message.</summary>
 467            /// <value>The enqueued sequence number of the message.</value>
 468            /// <remarks>
 469            /// For messages that have been auto-forwarded, this property reflects the sequence number
 470            /// that had first been assigned to the message at its original point of submission. This property is read-o
 471            /// </remarks>
 472            public long EnqueuedSequenceNumber
 473            {
 474                get
 475                {
 0476                    this.ThrowIfNotReceived();
 0477                    return this.enqueuedSequenceNumber;
 478                }
 479
 0480                internal set => this.enqueuedSequenceNumber = value;
 481            }
 482
 483            /// <summary>Gets or sets the date and time of the sent time in UTC.</summary>
 484            /// <value>The enqueue time in UTC. </value>
 485            /// <remarks>
 486            ///    The UTC instant at which the message has been accepted and stored in the entity.
 487            ///    This value can be used as an authoritative and neutral arrival time indicator when
 488            ///    the receiver does not want to trust the sender's clock. This property is read-only.
 489            /// </remarks>
 490            public DateTime EnqueuedTimeUtc
 491            {
 492                get
 493                {
 0494                    this.ThrowIfNotReceived();
 0495                    return this.enqueuedTimeUtc;
 496                }
 497
 0498                internal set => this.enqueuedTimeUtc = value;
 499            }
 500
 501            internal Guid LockTokenGuid
 502            {
 503                get
 504                {
 0505                    this.ThrowIfNotReceived();
 0506                    return this.lockTokenGuid;
 507                }
 508
 0509                set => this.lockTokenGuid = value;
 510            }
 511
 512            internal object BodyObject
 513            {
 12514                get;
 8515                set;
 516            }
 517
 518            void ThrowIfNotReceived()
 519            {
 4520                if (!this.IsReceived)
 521                {
 0522                    throw Fx.Exception.AsError(new InvalidOperationException());
 523                }
 4524            }
 525        }
 526    }
 527}