6

Current I'm using Microsoft.Azure.ServiceBus.IQueueClient to RegisterMessageHandler, and then the message I receive is of type Microsoft.Azure.ServiceBus.Message.

According to the documentation:

Message deferral APIs The API is BrokeredMessage.Defer or BrokeredMessage.DeferAsync in the .NET Framework client, MessageReceiver.DeferAsync in the .NET Standard client, and IMessageReceiver.defer or IMessageReceiver.deferAsync in the Java client.

...but none of those libraries seam to relate to the classes I'm actually using. How do I defer? What classes and stuff do I have to use in order to be able to defer messages? All the samples above dont give enough code snippets to explain it.

Update as requested by @Gaurav

from your answer, I can see my message has that property:

message.ScheduledEnqueueTimeUtc = DateTime.UtcNow.AddHours(1);

but the queueClient also has this method:

queueClient.ScheduleMessageAsync(message, DateTime.UtcNow.AddHours(1));

I'm going to try 'scheduledMessageAsync' as I cant see how to communicate that I've set ScheduledEnqueueTimeUtc without calling the queueClient

Ninjanoel
  • 2,864
  • 4
  • 33
  • 53

4 Answers4

7

Microsoft.Azure.ServiceBus.Message has a property called ScheduledEnqueueTimeUtc. Just set the value of this property to a date/time value in future when you want the message to appear in the queue. Message will be hidden till that time and will only appear in the queue at that date/time.

UPDATE

So I ran a test and confirmed that both ScheduledEnqueueTimeUtc and ScheduleMessageAsync works. I used version 4.1.1 for Microsoft.Azure.ServiceBus SDK.

Here's the code I wrote:

    static void Main(string[] args)
    {
        var connectionString = "my-connection-string";
        var queueName = "test";
        QueueClient queueClient = new QueueClient(connectionString, queueName);
        Message msg1 = new Message()
        {
            Body = Encoding.UTF8.GetBytes("This message has ScheduledEnqueueTimeUtc property set. It will appear in queue after 2 minutes. Current date/time is: " + DateTime.Now),
            ScheduledEnqueueTimeUtc = DateTime.UtcNow.AddMinutes(2)
        };
        queueClient.SendAsync(msg1).GetAwaiter().GetResult();
        Message msg2 = new Message()
        {
            Body = Encoding.UTF8.GetBytes("This message is sent via ScheduleMessageAsync method. It will appear in queue after 2 minutes. Current date/time is: " + DateTime.Now)
        };
        queueClient.ScheduleMessageAsync(msg2, new DateTimeOffset(DateTime.UtcNow.AddMinutes(2))).GetAwaiter().GetResult();
        Console.ReadLine();
    }

And this is what I see when I fetch the messages in Peek-Lock mode:

enter image description here

Gaurav Mantri
  • 128,066
  • 12
  • 206
  • 241
  • it's not working as expected, I'm setting that value but it's still sending loads of message over and over again. How to let Service Bus know that I've set that value? – Ninjanoel Feb 27 '20 at 17:19
  • i've updated my question with the code I've tried and what I'm about to try – Ninjanoel Feb 27 '20 at 17:28
  • Updated my answer. HTH. – Gaurav Mantri Feb 28 '20 at 03:24
  • thanks for the effort @Gaurav, but I'm receiving messages, not sending, and 'come back in a hour' wont work apparently (says the boss). I need the functionality that i've seen in hints of documentation that says I can reply to the message with defer and then close with a sequenceNumber at my discretion. – Ninjanoel Feb 28 '20 at 16:18
  • I think @Ninjanoel misunderstood the intent of this solution - it bypasses the complexity of deferring a message by reposting the original message. – cdonner Apr 16 '21 at 02:04
2

Using the message deferral APIs like BrokeredMessage.Defer or BrokeredMessage.DeferAsync will defer the message.

Defering a message will change the state of the message from Active to Deferred. The message can be later retrieved based on the sequence number.

ScheduleMessageAsync() is used to schedule the delivery of message (sends a message at specified time). It cannot be used after receiving a message.

Balasubramaniam
  • 371
  • 5
  • 14
1

I've coded the solution I was looking for, here is the basic outline:

inside an asynchronous method (runs its own thread)

public async Task InitialiseAndRunMessageReceiver()

start an infinite loop that reads the message

receiver = new MessageReceiver(serviceBusConnectionString, serviceBusQueueName, ReceiveMode.PeekLock); 
while (true) { var message = await receiver.ReceiveAsync(); ... more code... }

once you know you are about to start your long task, defer the message, but store the message.SystemProperties.SequenceNumber. this keeps it in the queue but prevents it from being re-delivered.

await receiver.DeferAsync(message.SystemProperties.LockToken);

and when you finally done ask for the message again using the message.SystemProperties.SequenceNumber, and complete the message as if it weren't deferred

var message = receiver.ReceiveDeferredMessageAsync(message.SystemProperties.SequenceNumber);
receiver.CompleteAsync(message.Result.SystemProperties.LockToken);

and your message will be removed from the queue.

much of my confusion was caused by the libraries being named similarly with overlapping lifespans.

Microsoft.Azure.ServiceBus.Core.MessageReceiver is the message receiver above

Ninjanoel
  • 2,864
  • 4
  • 33
  • 53
1

Old question, but what suited my situation was deleting the message and posting a copy using ScheduleMessageAsync (there is a copy method somewhere). Then the message would just come back at the desired time.

Rabbit
  • 1,741
  • 2
  • 18
  • 27