Azure-sdk-for-net: ServiceBusMessage Model

Created on 11 Feb 2020  路  28Comments  路  Source: Azure/azure-sdk-for-net

  • Decide whether to split into two model types (one for send, and another for receive)
  • Address comments from API View
  • Support all message properties from Track 1 (unless we specifically decide we don't want to support something)
Client Service Bus

All 28 comments

Historically, there was always some pain around BrokeredMessage/Message with regards to the broker set properties. Specifically, to be able to test certain scenarios w/o receiving a message but rather constructing it, an exception would be thrown. With some reflection, it could be worked around, but still wasn't straight forward. The idea of having two models could alleviate some of that.

Considering something like this:

public class Message
{

    public Message();
    public Message(ReadOnlyMemory<byte> body);
    public static string DeadLetterErrorDescriptionHeader;
    public static string DeadLetterReasonHeader;
    public ReadOnlyMemory<byte> Body { get; set; }
    public string ContentType { get; set; }
    public string CorrelationId { get; set; }
    public string Label { get; set; }
    public string MessageId { get; set; }
    public string PartitionKey { get; set; }
    public string ReplyTo { get; set; }
    public string ReplyToSessionId { get; set; }
    public DateTime ScheduledEnqueueTimeUtc { get; set; }
    public string SessionId { get; set; }
    public TimeSpan TimeToLive { get; set; }
    public string To { get; set; }
    public IDictionary<string, object> UserProperties { get; }
    public string ViaPartitionKey { get; set; }
    public Message Clone();
    public override string ToString();
}



public class ReceivedMessage : Message
{

    public ReceivedMessage();
    public ReceivedMessage(Memory<byte> body);
    public string DeadLetterSource { get; }
    public int DeliveryCount { get; }
    public long EnqueuedSequenceNumber { get; }
    public DateTime EnqueuedTimeUtc { get; }
    public DateTime ExpiresAtUtc { get; }
    public bool IsLockTokenSet { get; }
    public DateTime LockedUntilUtc { get; }
    public string LockToken { get; }
    public long SequenceNumber { get; }
}

``c# // Wasn'tMessage` considered too generic? 馃槈
public class Message
{
public Message() {}
public Message(ReadOnlyMemory body) {}
// DeadLetterErrorDescriptionHeader and DeadLetterReasonHeader are only applicable to a received message
//public static string DeadLetterErrorDescriptionHeader;
//public static string DeadLetterReasonHeader;
public ReadOnlyMemory Body { get; set; }
public string ContentType { get; set; }
public string CorrelationId { get; set; }
public string Label { get; set; }
public string MessageId { get; set; }
public string PartitionKey { get; set; }
public string ReplyTo { get; set; }
public string ReplyToSessionId { get; set; }
public DateTime ScheduledEnqueueTimeUtc { get; set; }
public string SessionId { get; set; }
public TimeSpan TimeToLive { get; set; }
public string To { get; set; }
public IDictionary UserProperties { get; }
public string ViaPartitionKey { get; set; }
public Message Clone() { return new Message(); }
public override string ToString() { return this.GetType().ToString(); }

// missing
public DateTime ExpiresAtUtc { get; set; }
// this would be AMQP translated message size, currently shows the body byte count
public long Size { get; }
// I don't use it, but others might
public long ForcePersistence { get; set; }

}

public class ReceivedMessage : Message
{
public ReceivedMessage() {}
public ReceivedMessage(Memory body) {}
public static string DeadLetterErrorDescriptionHeader;
public static string DeadLetterReasonHeader;
public string DeadLetterSource { get; }
public int DeliveryCount { get; }
public DateTime EnqueuedTimeUtc { get; }
public DateTime ExpiresAtUtc { get; }
public DateTime LockedUntilUtc { get; }
public string LockToken { get; }
public long SequenceNumber { get; }
public long EnqueuedSequenceNumber { get; }

// IsLockTokenSet was necessary to distinguish between newly created Message vs received one.
// With two separate models, it seems unnecessary
//public bool IsLockTokenSet { get; }

// these properties also need to be on the received size as well
public long Size { get; }
public string ContentType { get; set; }
public string CorrelationId { get; set; }
public string Label { get; set; }
public string MessageId { get; set; }
public string PartitionKey { get; set; }
public string ReplyTo { get; set; }
public string ReplyToSessionId { get; set; }
public DateTime ScheduledEnqueueTimeUtc { get; set; }
public string SessionId { get; set; }
public TimeSpan TimeToLive { get; set; }
public string To { get; set; }

// missing
public MessageState State { get; set; }

}

public enum MessageState
{
Active = 0,
Deferred = 1,
Scheduled = 2
}
```

Thanks @SeanFeldman

Anytime

Regarding Size and ContentType, keep in mind that ServiceBusReceivedMessage derives from ServiceBusMessage.

ExpiresAtUtc is on the ServiceBusReceivedMessage.
Size is on ServiceBusMessage.

Will update that in this issue.

```c#
public class Message
{
public Message() {}
public Message(ReadOnlyMemory body) {}
public ReadOnlyMemory Body { get; set; }
public string ContentType { get; set; }
public string CorrelationId { get; set; }
public string Label { get; set; }
public string MessageId { get; set; }
public string PartitionKey { get; set; }
public string ReplyTo { get; set; }
public string ReplyToSessionId { get; set; }
public DateTime ScheduledEnqueueTimeUtc { get; set; }
public string SessionId { get; set; }
public TimeSpan TimeToLive { get; set; }
public string To { get; set; }
public IDictionary UserProperties { get; }
public string ViaPartitionKey { get; set; }
public Message Clone() { return new Message(); }
public override string ToString() { return this.GetType().ToString(); }

// this would be AMQP translated message size, currently shows the body byte count

public long Size { get; }

}

public class ReceivedMessage : Message
{
public ReceivedMessage() {}
public ReceivedMessage(Memory body) {}
public static string DeadLetterErrorDescriptionHeader;
public static string DeadLetterReasonHeader;
public string DeadLetterSource { get; }
public int DeliveryCount { get; }
public DateTime EnqueuedTimeUtc { get; }
public DateTime ExpiresAtUtc { get; }
public DateTime LockedUntilUtc { get; }
public string LockToken { get; }
public long SequenceNumber { get; }
public long EnqueuedSequenceNumber { get; }
public DateTime ExpiresAtUtc { get; set; }
public string CorrelationId { get; set; }
public string Label { get; set; }
public string MessageId { get; set; }
public string PartitionKey { get; set; }
public string ReplyTo { get; set; }
public string ReplyToSessionId { get; set; }
public DateTime ScheduledEnqueueTimeUtc { get; set; }
public string SessionId { get; set; }
public TimeSpan TimeToLive { get; set; }
public string To { get; set; }
}
```

It looks like the only deltas from your comments are the ForcePersistence and State properties. I didn't see these on Track 1 so I will need to dig into these a bit more.

Sounds like the State property would be useful for customers.

Sounds like the State property would be useful for customers.

You're in an entertaining mood today, @JoshLove-msft...

Only outstanding issue is how to get the State populated.

Only outstanding issue is how to get the State populated.

Having an issue number with that sentence would be very helpful.

Only outstanding issue is how to get the State populated.

Having an issue number with that sentence would be very helpful.

It is this issue. I am leaving it open since this is not yet resolved.

It reads as if that a separate chunk of work, unrelated to this issue. My bad.

It reads as if that a separate chunk of work, unrelated to this issue. My bad.

No worries. I should have said only outstanding "item"

We've had a slight change with this. With using inheritance, users would be able to set properties directly on a received message (unless we add runtime validation or hide each of the properties with "new" keyword). Also, users would be able to re-send a received message which presents the issue of whether or not they might be confused as to what happens to the already set system-level properties on the message. Track 1 handled this by forcing users to Clone a message to get a new instance with the system properties cleared out. In order to address both of these concerns, we are thinking of breaking the inheritance, so ServiceBusReceivedMessage would no longer derive from ServiceBusMessage. In order to allow users to re-send a received message, we will have a static factory method ServiceBusMessage.CreateFrom(ServiceBusReceivedMessage) that returns a ServiceBusMessage.
I will create a separate issue to go through all of our design considerations for the message types, but just wanted to add this here as well. Once the issue is created, I will link it here as well.

Created https://github.com/Azure/azure-sdk-for-net/issues/10829

@JoshLove-msft you're linking this to #10826, which is said to "solicit feedback from the community", closed 5h after it was raised. Was someone WFH and drinking? 馃榾

@JoshLove-msft you're linking this to #10826, which is said to "solicit feedback from the community", closed 5h after it was raised. Was someone WFH and drinking? 馃榾

Nope I can tell you that I am not drinking.. I created a separate issue to avoid having some internal housekeeping comments clutter the issue. Sorry for the confusion.

I'm not judging. There are days when the flow is better when liquified.

I'm not judging. There are days when the flow is better when liquified.

Hah fair enough..

Closing this out as we have https://github.com/Azure/azure-sdk-for-net/issues/10241 regarding exposing State property.

Issue #10241 looks much larger than just the problem of enabling users to create fake instances of Message for testing (as described here: https://github.com/Azure/azure-service-bus-dotnet/issues/691). Heck, even just making the SequenceNumber's setter public instead of internal would unblock a whole world of test mocking. Could we reactivate this issue to track making the Message class at least a bit more reusable in tests?

@nerdile With the message split into two, creating a ServiceBusMessage is not an issue. And for testing purposes, ServiceBusModelFactory allows constructing ServiceBusReceivedMessage to fake broker set properties. I think that's what you're after.

I see, so this is fixed in the new Azure.Messaging.ServiceBus package, but I was using Microsoft.Azure.ServiceBus. Will try with the new preview. Thanks for the clarification!

For Microsoft.Azure.ServiceBus you'd need to play dirty with some reflection.

Was this page helpful?
0 / 5 - 0 ratings