-1

Before I start, the closest I could find for this here was How do you implement an async action delegate method? however, being a bear of a somewhat small brain, I couldn't get my braincells around the answer and how it might apply to my current issue.

I'm writing a wrapper around the RabbitMq C# Client, and am having a problem with my delegates and the async events.

This bit of code seems fine:

private Action<object, BasicAckEventArgs> _syncHandlerBasicAck;
private Action<object, BasicNackEventArgs> _syncHandlerBasicNack;

Defining the code here:

_syncHandlerBasicAck = delegate(object o, BasicAckEventArgs args)
{
    //[Do stuff here]
};

_syncHandlerBasicNack = delegate(object o, BasicNackEventArgs args)
{
    //[Do stuff here]
};

then using it with:

Channel.BasicAcks += _syncHandlerBasicAck.Invoke;
Channel.BasicNacks += _syncHandlerBasicNack.Invoke;

The issue I have is with the async BasicDeliver event. This is my current code:

private Func<object, BasicDeliverEventArgs, Task> _syncHandlerIncomingMessage;

followed by

_syncHandlerIncomingMessage = async delegate(object o, BasicDeliverEventArgs args)
{
    //[Do stuff here]
};

however whenever I try to do:

Consumer.Received += _syncHandlerIncomingMessage.Invoke;

I get a System.NullReferenceException telling me that

Object reference not set to an instance of an object.

I see that there's a BeginInvoke method available but I'm not sure that applies to my current situation since I'm not using Callbacks and just want to call this asynchronously.

Anyone got any pointers? (I'll even take "Duplicate question: Answer here" type responses as I'm sure I may have missed something).

Edit

I had two issues; one with wiring this up, the other where some refactoring got in my way! Thanks so much to @Coolbots for the answer and @Camilo to show me the error of my ways.

Rachel Ambler
  • 1,440
  • 12
  • 23
  • I am confused with your way of subscribing to events. Can you not do `Channel.BasicAcks += (object o, BasicAckEventArgs args) => { //[Do stuff here] };` and/or `Channel.BasicAcks += async (object o, BasicAckEventArgs args) => { //[Do stuff here] };`? – GSerg Aug 10 '20 at 17:25
  • I'm trying to create some cleaner code @GSerg. I originally had it that way and my outer method was getting kinda hairy. That said, I'd still like to know how you can do this because it would _seem_ to me that you _should_ be able to do it this way. – Rachel Ambler Aug 10 '20 at 17:31
  • Can your `async delegate` be it's own method returning `async Task` (instead of an anonymous method stored in `_syncHandlerIncomingMessage`), and that method be assigned to `Consumer.Received`? – CoolBots Aug 10 '20 at 17:44
  • Care to expand upon that with an answer, @CoolBots ? – Rachel Ambler Aug 10 '20 at 17:45
  • Can you add the signature for `Received`? – Camilo Terevinto Aug 10 '20 at 17:45
  • @CamiloTerevinto confused what you mean: As supplied in my delegate it's `Consumer.Received += delegate(object sender, BasicDeliverEventArgs ea)` – Rachel Ambler Aug 10 '20 at 17:47
  • What I mean is, what's the type of `Consumer.Received`? Is it `EventHandler`? – Camilo Terevinto Aug 10 '20 at 17:48
  • Apparently it's `AsyncEventHandler` @CamiloTerevinto – Rachel Ambler Aug 10 '20 at 17:52
  • 1
    I don't think the error comes from the posted code. I just tested this on a console app (without RabbitMQ dependencies) and it works fine with `Consumer.Received += x.Invoke;` – Camilo Terevinto Aug 10 '20 at 17:59
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/219560/discussion-between-rachel-ambler-and-camilo-terevinto). – Rachel Ambler Aug 10 '20 at 17:59
  • @Coolbots - add your answer back again. There were two issues - one was fixed with your answer, the other Camilo helped me with finding (see chat) – Rachel Ambler Aug 10 '20 at 18:23

1 Answers1

2

The problem is event subscriptions do not support async delegate directly, so a wrapper is needed, like so:

Alternative syntax (see EDIT):

async Task MyDelegate(object sender, BasicDeliverEventArgs e)
{
   // Do stuff 
} 

Consumer.Received += async (s, e) => await MyDelegate(s, e);

I'm on my phone (can't test code), so while I'm certain this works with an async Task MyDelegate(...), I'm not 100% this will work with async delegate MyDelegate(...); although, in theory, it should.

EDIT: Deleted, then restored my answer per OP's request (I'm glad it was of some value); finally got to a computer, and verified that all 3 subscription approaches work, as verified by @CamiloTerevinto in the commments, and reason for my original deletion of this answer:

Func<object, BasicDeliverEventArgs, Task> _syncHandlerIncomingMessage;

_syncHandlerIncomingMessage = async delegate (object sender, BasicDeliverEventArgs eventArgs)
{
   await Task.Delay(1000); // Do stuff
};

// OR

async Task SyncHandlerIncomingMessage(object sender, BasicDeliverEventArgs eventArgs)
{
   await Task.Delay(1000); // Do stuff
} 

// Can be subscribed as follows:

Consumer.Received += _syncHandlerIncomingMessage.Invoke; // as posted by OP

// OR

Consumer.Received += async (s, e) => await SyncHanlderIncomingMessage.Invoke(s, e);

// OR

Consumer.Received += SyncHandlerIncomingMessage;

In summary: no wrapper necessary. I am glad my original answer was of value to the OP; I am adding this edit for completeness/correctness of the answer, in hopes that it's of use to others.

CoolBots
  • 4,770
  • 2
  • 16
  • 30
  • Same error: `Consumer.Received += async (s, e) => await _syncHandlerIncomingMessage(s, e);` – Rachel Ambler Aug 10 '20 at 17:56
  • Still no dice with `Consumer.Received += async (s, e) => await _syncHandlerIncomingMessage.Invoke(s, e);` - still same error – Rachel Ambler Aug 10 '20 at 17:59
  • Ok, so it doesn't appear possible with `async delegate` then. I'll update my answer. – CoolBots Aug 10 '20 at 18:00
  • @CoolBots What you show in your answer are other ways of doing this, but it still works as shown in the question code (tested in a Console app with delays and messages) – Camilo Terevinto Aug 10 '20 at 18:01
  • @CoolBots I think we didn't understand each other. I meant that the event registration *works*, the OP's problem comes from another place – Camilo Terevinto Aug 10 '20 at 18:09
  • @CamiloTerevinto ohh! Got it - so the OPs original code works as posted then - is the wrapper not required at all? I need to stop doing this from my phone, lol – CoolBots Aug 10 '20 at 18:10
  • Nope, this works fine for me `Consumer.Received += x.Invoke;`, no need to wrap – Camilo Terevinto Aug 10 '20 at 18:12
  • @CamiloTerevinto thank you so much, I learned something new! I'm going to delete my answer, as it is entirely incorrect. – CoolBots Aug 10 '20 at 18:14