5

I have a trouble marking my BrokeredMessage as Complete.

Simple code that works like expected:

 private void OnMessageArrived(BrokeredMessage message)
 {
     var myObj= message.GetBody<MyObject>();

     //do things with myObj

     message.Complete();            
 }

When I try to wait for user to finish with myObj I get exception:

brokeredmessage has been disposed

Code below:

 private Dictionary<long, BrokeredMessage> ReceivedMessages;      
 ReceivedMessages = new Dictionary<long, BrokeredMessage>();

 private void OnMessageArrived(BrokeredMessage message)
 {
     var myObj= message.GetBody<MyObject>();
     ReceivedMessages.Add(myObj.Id, message);

     //do things with myObj      
 }

 private void Button_Click(object sender, RoutedEventArgs e)
 {
     // get myObj on which user clicked

     ReceivedMessages[myObj.Id].Complete();
     ReceivedMessages.Remove(myObj.Id);        
 }

To me it looks like ServiceBus somehow lose connection to actual object in c#

Something similar to detached object in EF, just in this case object is detached from ServiceBus

EDIT:

It is important to me to mark message as complete only after user click button. In case AC goes down (or similar things) I want messages still remain on Service Bus Topic so that next time user start app he will again receive messages that he did not processed.

topolm
  • 385
  • 7
  • 13

4 Answers4

2

You should not store the brokeredmessage itself. When you call the .Complete() method, this is what happens according to the documentation:

Completes the receive operation of a message and indicates that the message should be marked as processed and deleted

What you should do instead is to store MyObject type of objects in your dictionary as brokered messages will be destroyed upon completion.

private Dictionary<long, MyObject> ReceivedMessages;      
ReceivedMessages = new Dictionary<long, MyObject>();

And in the relevant bit of code:

 var myObj= message.GetBody<MyObject>();
 ReceivedMessages.Add(myObj.Id, myObj);
Pedro G. Dias
  • 3,162
  • 1
  • 18
  • 30
  • Could you recommend a solution to this problem case then: 1. Message received, its data broken into pieces and sent to asynchronous processing. We want to lock the message until its data is processed by the user that acquired it. 2. if an exception occurs or the user quits the program, unlock the message so some other user can process it later 3. If/when the final piece of data is processed, then remove the message from the ServiceBus since its data was properly processed. – ErroneousFatality Mar 07 '16 at 16:04
  • Yes. You hold off calling Complete() on the message until you are done. – Pedro G. Dias Mar 07 '16 at 16:09
  • Excuse me if I'm wrong, but that's exactly what OP is doing. He saves the message in a dictionary so he can call Complete after the async processing is over. You recommended that he instead not do that and only save the data he's processing. How would he then call Complete on the message after the async process? – ErroneousFatality Mar 07 '16 at 16:17
  • - what basically is happening is that the OP wants to introduce MANUAL clearing of a message from the service bus, in which case he is doing the right thing. But to me, it is the wrong thing to do, because manually clearing a message from a bus is an architecture that leaves a ton of questions in the air. What if the user goes for a pee break? For the reception of the message, you should do this fast and NOT depending on manual checking. Instead, you could have an answer queue to post your answer to on the click event, if your system requires an answer from a user. – Pedro G. Dias Mar 07 '16 at 16:25
  • 1
    I truly thank you for your patience. Thus I feel obliged to properly explain our (mine and OPs) system to you, without any expectation of your further help, unless you want to. The ServiceBus queue is filled with tickets a distributed network of moderators need to inspect, modify if need be, and finally approve or decline it via a GUI. That's why we want to keep the messages in the Bus after getting their content up until the moderator is finished with them. – ErroneousFatality Mar 07 '16 at 16:36
  • 1
    I believe that what you want is Brokered Message sessions. Here essentially you enable Sessions for a Queue/Subscription and then rather than processing messages you call AcceptMessageSession. The SessionID property of a message will determine what session it belongs to. Each session has a state available that you can access thru GetState and SetState on MessageSession. More information here: https://code.msdn.microsoft.com/Brokered-Messaging-Session-41c43fb4 – Pedro G. Dias Mar 07 '16 at 17:02
2

This is very implicit and not stated well in the documentation as far as I know, but when you subscribe to a SB Topic/Subscription, you can only access the BrokeredMessage object within the execution scope of the OnMessage callback delegate you supply to SubscriptionClient.OnMessage.

Once the callback returns, the BrokeredMessage is disposed.

If you want to cache the message and mark it as complete/abandoned/deadletter, you should cache the BrokeredMessage.LockToken, along with any other property you need like the message body, and use the

  • SubscriptionClient.Complete
  • SubscriptionClient.Abandon
  • SubscriptionClient.Deadletter

which receive a LockToken as a parameter, when you want to mark them as complete.

Bob
  • 101
  • 1
  • 3
  • 8
2

I found this to be the same issue if you are doing an async method, the messages is dispatched and by the time the await happens, the message is already disposed. I only wish I found this post earlier..

0

Take a look at ServiceBus LockToken.

You can call Complete(lockToken)

Stephane
  • 11,056
  • 9
  • 41
  • 51