Skip to content

Instantly share code, notes, and snippets.

@jsquire
Created June 5, 2020 17:10
Show Gist options
  • Save jsquire/9a6e66feaab250dd36a65d46d70e9542 to your computer and use it in GitHub Desktop.
Save jsquire/9a6e66feaab250dd36a65d46d70e9542 to your computer and use it in GitHub Desktop.
Brainstorming: Service Bus Message Transport Body

Brainstorming: Transport message format

Scenarios

Retrieve the transport body of a message

using Azure.Messaging.SerivceBus.Transport.Amqp;

var message = DoSomethingToCreateTheServiceBusMessage();
var amqpBody = message.GetAmqpBody();

var data = amqpBody.Data;  // ReadOnlyMemory<byte>
var value = amqpBody.Value; // object
var sequence = amqoBody.Sequence;  // IEnumerable<IList<object>>

Create a message with an explicit AMQP data body

using Azure.Messaging.SerivceBus.Transport.Amqp;

var body = new AmqpMessageBody { Data = Encoding.UTF8.GetBytes("JSON STUFF") };
var message = body.CreateServiceBusMessage();

Create a message with an explicit AMQP value body

using Azure.Messaging.SerivceBus.Transport.Amqp;

var valueBody = new AmqpMessageBody { Value = DateTimeOffset.UtcNow };
var valueMessage = (ServiceBusMessage)valueBody;

Create a message with an explicit AMQP sequence body

using Azure.Messaging.SerivceBus.Transport.Amqp;

var sequence = new[]
{
    new List<object> { 1, 2, 3 },
    new List<object> { 7, 8, 9 }
};

var sequenceBody = new AmqpMessageBody { Sequence = sequence};

var sequenceMessage = new ServiceBusMessage();
sequenceMessage.SetAmqpBody(sequenceBody);

Type Skeletons

Azure.Messaging.SerivceBus.Transport.Amqp

// Look!  I stole Josh's stuff...
public class AmqpMessageBody : TransportMessageBody
{
    public ReadOnlyMemory<byte> Data { get; set; }
    public IEnumerable<IList<object>> Sequence { get; set; }
    public object Value { get; set; }
    
    // Not in love with the name, other ideas for a way to create?
    public ServiceBusMessage CreateServiceBusMessage() => new ServiceBusMessage 
    { 
        TransportBody = this,
        TransportFormat = TransportMessageFormat.Amqp            
    };

    internal override ReadOnlyMemory<byte> GetBinaryData() => Data;        
    
    // This may be a bad idea; I'm not sure how I feel about it.
    public static explicit operator ServiceBusMessage(AmqpMessageBody body) => 
        body.CreateServiceBusMessage();        
}

// ... and I stole Yanni's idea!
public static class ServiceBusMessageExtensions
{
    public static AmqpMessageBody GetAmqpBody(this ServiceBusMessage instance) => (AmqpMessageBody)instance.TransportBody;

    public static void SetAmqpBody(this ServiceBusMessage instance, AmqpMessageBody body)
    {
        // Argument.AssertNotNull(body);
        
        instance.TransportBody = body;
        instance.TransportFormat = TransportMessageFormat.Amqp;
    }
}

Azure.Messaging.SerivceBus.Transport

// Only exists to allow the Service Bus Message to query a transport body for a binary representation
// to use for the `Body` property.  Not sure how I feel about this...
public abstract class TransportMessageBody
{
    internal abstract ReadOnlyMemory<byte> GetBinaryData();
}

Azure.Messaging.ServiceBus.Core

// Used to denote that a Service Bus Message has a transport-specific body applied;
// intended to be used by the message converter to ensure it can translate.
internal enum TransportMessageFormat
{
    Amqp
}

Azure.Messaging.ServiceBus

// Only showing new or updated members; there's a bunch of existing surface not shown.
public class ServiceBusMessage
{
    private ReadOnlyMemory<byte> _body;
    
    internal TransportMessageBody TransportBody { get; set; }
    
    // If this is `null` then no transport-specific body has been set.
    internal TransportMessageFormat? TransportFormat { get; set; }

    public ReadOnlyMemory<byte> Body
    {
        get
        {
            if (!_body.IsEmpty)
            {
                return _body;
            }
            
            if (TransportFormat.HasValue)
            {
                return TransportBody.GetBinaryData();
            }
            
            return default;
        }

        set
        {
            if (TransportFormat.HasValue)
            {
                throw new InvalidOperationException("Transport-specific body has already been specified.");
            }
            
            _body = value;
        }
    }
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment