antonyvorontsov / RabbitMQ.Client.Core.DependencyInjection

.Net Core library-wrapper of RabbitMQ.Client for Dependency Injection.
MIT License
111 stars 36 forks source link

A circular dependency when using IQueueService inside an handler. #28

Closed jerpic closed 4 years ago

jerpic commented 4 years ago

Hello Antony,

Many thanks for your lib, it's a great job.

I'm trying to use it for a projet, but I'm facing with this issue. In a ConsumerHandler (implementing the IMessageHandler), I would like inside the method "Handle(string message, string routingKey)" to send a new message to another queue. I tried to inject the IQueueService in the constructor of the ConsumerHandler but I got a circular dependency injection at run time.

Do you have any idea about how I can solve or implement this kind of feature?

Best Regards,

antonyvorontsov commented 4 years ago

Hi jerpic!

Some time ago I faced the same issue as you and this resulted to the appearance of the couple additional message handler interfaces.

You can make a message handler that implements INonCyclicMessageHandler interface, which looks like this.

public interface INonCyclicMessageHandler
{
    void Handle(string message, string routingKey, IQueueService queueService);
}

The interface itself is quite familiar to IMessageHandler, but the only difference is that IQueueService is passed to the Handler method as a parameter.

So you can do something like this

public class MyMessageHandler : INonCyclicMessageHandler
{
    // Inject anything you want except IQueueService.
    readonly ILogger< MyMessageHandler > _logger;
    public MyMessageHandler(ILogger< MyMessageHandler > logger)
    {
        _logger = logger;
    }

    public void Handle(string message, string routingKey, IQueueService queueService)
    {
        // Send anything you want using queueService instance.
        var anotherMessage = new MyMessage { Foo = "Bar" };
        queueService.Send(anotherMessage, "exchange.name", "routing.key");
    }
}

There is another interface that you can use - IAsyncNonCyclicMessageHandler. It provides the async version of the same method.

 public interface IAsyncNonCyclicMessageHandler
 {
    Task Handle(string message, string routingKey, IQueueService queueService);
 }

And you can implement your custom message handler like this:

public class MyMessageHandler : IAsyncNonCyclicMessageHandler
{
    // Inject anything you want except IQueueService.
    readonly ILogger< MyMessageHandler > _logger;
    public MyMessageHandler(ILogger< MyMessageHandler > logger)
    {
        _logger = logger;
    }

    public async Task Handle(string message, string routingKey, IQueueService queueService)
    {
        // Send anything you want using queueService instance.
        // You can also do it asynchronously
        var anotherMessage = new MyMessage { Foo = "Bar" };
        await queueService.SendAsync(anotherMessage, "exchange.name", "routing.key");
        _logger.LogInformation("The message has been sent back.");
    }
}

There are DI extension methods which allow you to register those message handler:AddNonCyclicMessageHandlerTransient, AddNonCyclicMessageHandlerSingleton, AddAsyncNonCyclicMessageHandlerTransient, AddAsyncNonCyclicMessageHandlerSingleton.

You can take a look at the documentation file as well. Everything I said is covered there.

The reason I moved the documentation to the separate directory is quite simple. At time the main readme.md file became too long that I decided to make docs and move them from master readme. That is why sometimes it is not unclear how to do this or that before looking deeply into documentation.

Best regards, Antony

jerpic commented 4 years ago

Hi Antony,

Many thanks for your answer, you save my day!!!

Again you do a great work!

Best Regards, Jérôme.