I am writing an ACME worker service that uses an asynchronous request-reply communication model. In Azure Service Bus I have a queue acme-requests
for all inbound requests to the ACME service. I also have a queue acme-replies
for all inbound replies to requests that the ACME service has made to other services and their respective request queues.
For example, when the ACME service sends out a request to a Foo service, it would generate a GUID for this request-reply message session and store it in its local database. It then sends a message to the queue foo-requests
. The Foo service would respond to the queue acme-replies
later.
In the ACME service, I have a message inbox BackgroundService
receiving messages from acme-replies
and dispatching them to my message handlers.
More precisely, in theBackgroundService
's process loop I would like to call ServiceBusSessionReceiver replyReceiver = await client.AcceptNextSessionAsync("acme-replies")
, process the message and then close the current message session. However, that seems to go against the official recommendation:
The Service Bus objects that interact with the service, such as ServiceBusClient, ServiceBusSender, ServiceBusReceiver, and ServiceBusProcessor, should be registered for dependency injection as singletons (or instantiated once and shared). [...] We recommend that you don't close or dispose these objects after sending or receiving each message.
(Note that ServiceBusSessionReceiver
is a subclass of ServiceBusReceiver
.)
My questions now is how to combine request-reply, message sessions and dependency injection?
In particular I have the following questions:
How do I handle the lifetimes of ServiceBusSessionReceiver
and message session? Their lifetimes seem to be identical.
In consequence, should a ServiceBusSessionReceiver
instance really be a singleton or rather a scoped service?
Can I even create a ServiceBusSessionReceiver
instance without making it contact the Service Bus immediatly? There is only a method ServiceBusClient.AcceptSessionAsync
but no method ServiceBusClient.CreateSessionReceiver
.
The ServiceBusSessionReceiver
is somewhat unique in that it is usually short-lived as applications cycle between sessions. The reason is exactly what you've pointed out - that the session receiver and session itself have the same lifetime. You cannot create a ServiceBusSessionReceiver
without binding it to an actual session via service operation.
If your application is going to continually read from a well-known session, registering the session receiver as a singleton makes sense. However, that's less common than the pattern of "read from a session until empty and then move to a new session."
In this scenario, your best bet is likely to register the ServiceBusClient
as a singleton with DI. Create a ServiceBusSessionReceiver
when requesting a session and pass it around as an instance, as needed, until you're done with the session.
I'll reach out to the MS Docs team to see if we can't get that content adjusted. In my opinion, it's misleading as written.