I have an interesting problem and I am new to async programming, so any criticism is accepted. I have 2 APIs communicating with each other. The main idea for what I am trying to create is when a user tries to complete his shopping cart - the cart API asks the catalogue API for available quantities. And since it is a message (RabbitMQ) I want to wait for this message to be received back to my cart API, processed and then return to the control flow of the method in the cart API for payment execution to continue. I have 3 classes which are involved: CheckoutShoppingCart - which executes the payment, asks Catalogue API for available quantities. SessionState - holds temporary data for a product/ logged user (Singleton, injected in the 2 classes). EventProcessor - processes incoming RabbitMQ messages.
According to the official documentation of RabbitMQ they use something called correlationId. But I decided to create a workaround, I succeeded to some degree, but the code fails for a very unexpected reason.
The first method below is the one which sends message through RabbitMQ to the Catalogue API.
public class CheckoutShoppingCart
{
await PublishMessageAvailableQuantities(loggedUserCart.Products);
await ProductQuantityEvaluation();
}
Then the second one is the one who is failing and this is its body:
private async Task ProductQuantityEvaluation()
{
Task.WhenAny(sessionState.CompleteTask());
}
Now, the task within WhenAny - sessionState.CompleteTask() is a dummy method which I created as a flag in the message processor class I have, which processes information messages received from the catalogue API. When the message is successfully received the processor executes this dummy method. And thats why I follow its status - to see if it is executed.
So, when someone clicks to execute the payment of the shopping cart, I want to reach the Task.WhenAny which would wait for this task to be completed by the message processor class, which means that the message for the available quantities is received. When it is received the control flow would continue in the class which executes the payment of the shopping cart.
Everything is working with Task.Wait(10000). While this task is waiting, other thread works on the processing of the message, when it is done the control flow returns here. But when I use the WhenAny, when I debug - for some reason the WhenAny(sessionState.CompleteTask()) invokes the method itself, and when I debug the method sessionState.CompleteTask() it actually gets invoked before the event processor invocation. But from code perspective it is only invoked from the message processor.
One of the cases of the Event processor:
public class EventProcessor
{
case AppConstants.eventTypeSendActualProductQuantities:
var disapprovedProductInfo = JsonSerializer.Deserialize<PublishedProductModel>(message);
sessionState.publishedProductModel = disapprovedProductInfo;
sessionState.productQuantityStatus = 2;
**await sessionState.CompleteTask();**
break;
}
This is the dummy method which is only used as a flag.
public class SessionState
{
public async Task CompleteTask()
{
var tcs = new TaskCompletionSource<bool>();
tcs.SetResult(true);
}
}
This is how I check if the above method is completed:
private async Task ProductQuantityEvaluation()
{
Task.WhenAny(sessionState.CompleteTask());
}
So, the code is working perfectly with Wait(miliseconds) but when I want to wait for completion of another method using WhenAny(); it does not work. What am I missing?