17

I am working on the app using .NET Core 2 and MassTransit 4(dev).

Mass Transit requires parameterless constructor for consumers. I need to use e.g. logger, dbContext etc in my consumers and I would like to keep using native DI from .NET Core so I would prefer not to have to add Autofac etc (if possible?). It is causing the issue when .NET Core DI cannot inject my deps because my consumer is created with parameterless constructor...

Is there any way to resolve dependencies using native net core DI while having parameterless constructor OR is there some way of declaring endpoints of MassTransit which would allow to do that other than standard:

sbc.ReceiveEndpoint(host, Configuration["QueueName"], 
                    e => { e.Consumer<MyAwesomeConsumer>();});

I have tried:

// =========== MASS TRANSIT ===============
            var serviceProvider = services.BuildServiceProvider();
            services.AddSingleton<MyAwesomeConsumer, MyAwesomeConsumer>();
            var bus = Bus.Factory.CreateUsingRabbitMq(sbc =>
            {
                var host = sbc.Host(new Uri("rabbitmq://localhost"), h =>
                {
                    h.Username("guest");
                    h.Password("guest");
                });

                sbc.ReceiveEndpoint(host, Configuration["QueueName"],
                    e =>
                    {
                        e.Consumer(typeof(MyAwesomeConsumer), serviceProvider.GetService);
                    });
            });

Application lifts up but as soon as first message arrives I am getting an error: MassTransit.ConsumerException: Unable to resolve consumer type 'AuthService.Integrations.MyAwesomeConsumer'.

I will appreciate any help. I hope it's clear what I am asking about.

Marek Urbanowicz
  • 12,659
  • 16
  • 62
  • 87

2 Answers2

22

If you are using the Microsoft.Extensions.DependencyInjection container (which is the default with ASP.NET Core), then you will need the MassTransit.Extensions.DependencyInjection package for MassTransit. Note that the package is currently only available as a pre-release version and also requires a pre-release version of MassTransit. Furthermore, the new package is part of a massive migration for .NET Standard, which still isn’t complete, so you should probably be a bit careful as there are likely going to be quite a few moving parts until the final release.

Since the package isn’t officially released, there also isn’t any documentation on it yet. But from looking at the pull request that introduced it and the current source, I think what you need to do is the following:

In Startup.ConfigureServices, you need to add MassTransit, just like you would add other services:

services.AddMassTransit(c =>
{
    c.AddConsumer<MyConsumer>();
    c.AddConsumer<MyOtherConsumer>();

    // or sagas
    c.AddSaga<MySaga>();
});

// you still need to register the consumers/sagas
services.AddScoped<MyConsumer>();
services.AddScoped<MyOtherConsumer>();
services.AddSingleton<ISagaRepository<MySaga>, InMemorySagaRepository<MySaga>>();

In the configure action, you need to register you consumers and sagas in order for MassTransit to resolve them properly later.

Then, in your bus configuration, you would call LoadFrom with the service provider:

sbc.ReceiveEndpoint(host, Configuration["QueueName"], e =>
{
    e.LoadFrom(serviceProvider);
});

As for where you should do this (also in order to have access to the service provider), I would suggest you to do it in your Startup’s Configure method. The method can take any dependency as an argument, so you can easily inject the service provider there:

public void Configure(IApplicationBuilder app, IServiceProvider serviceProvider, IApplicationLifetime applicationLifetime)
{
    var bus = Bus.Factory.CreateUsingRabbitMq(sbc =>
    {
        var host = sbc.Host(new Uri("rabbitmq://localhost"), h =>
        {
            h.Username("guest");
            h.Password("guest");
        });

        sbc.ReceiveEndpoint(host, Configuration["QueueName"], e =>
        {
            e.LoadFrom(serviceProvider);
        });
    });

    // start/stop the bus with the web application
    applicationLifetime.ApplicationStarted.Register(bus.Start);
    applicationLifetime.ApplicationStopped.Register(bus.Stop);
}

One final disclaimer though: I personally haven’t used MassTransit, so I don’t really know if this makes actual sense for the library.

poke
  • 369,085
  • 72
  • 557
  • 602
  • promising and it's pointing in right direction but not working... `services.AddMassTransit(typeof(Startup));` giving error on compilation: Startup.cs(114,37): error CS1503: Argument 2: cannot convert from 'System.Type' to 'System.Action' – Marek Urbanowicz Dec 23 '17 at 17:25
  • Meh, I was almost expecting that. The overloads on that method are really *bad*. Try `AddMassTransit(new Type[] { typeof(Startup) })` then. – poke Dec 23 '17 at 17:26
  • Okay, I just tried it myself, and it seems that the code has been changed quite a bit since that original pull request. I’ve updated my answer to reflect the current ways to register the consumers. This should hopefully work now. – poke Dec 23 '17 at 17:43
  • `c.AddSaga();` but not the Automatonymous sagas – smg Dec 23 '17 at 18:03
  • there is new commit that adds support of Automatonymous https://github.com/MassTransit/MassTransit/commit/0b6945cd7106025a3b851670b3556948ad5e7975 – smg Dec 23 '17 at 18:39
  • @poke - awesome. That was quick. Could you explain me how did you get to the solution? I'm keen to learn – Marek Urbanowicz Dec 23 '17 at 18:53
  • @MU Glad it’s working! :) – Basically, considering that I have no idea how the library works or how it’s used (which made it a bit difficult), I started at the manual, looking whether there was any built-in way for dependency injection when I found the [“Use IoC container”](http://masstransit-project.com/MassTransit/usage/containers/) chapter hinting at support for various DI containers. So then I looked at its source to figure out how that was actually working internally. And then I searched the issues to see if there was any attempt to get the Extensions.DI integrated. – poke Dec 23 '17 at 19:17
  • @MU That got me that pull request. And from the changes there, especially the extensions class for `IServiceCollection` and the tests, I assumed how you would use it (especially since it would likely work like *any* of the other DI containers). Unfortunately, I did not bother to check the *current* source for any changes which is why the initial solution did not work. But the current source (and its tests) then gave enough information on how it should likely work. – poke Dec 23 '17 at 19:19
  • @smg I’m not sure what you are trying to tell me with that, but then again, I don’t really know MassTransit (nor Automatonymous). Are you suggesting to change the `AddSaga` call into something else? Feel free to edit the code in my post if you have more insight about it! – poke Dec 23 '17 at 19:20
  • Until yesterday statemachine sagas (the main way to do sagas as I know) was not supported – smg Dec 23 '17 at 19:23
  • @smg Interesting! I guess one should be careful with using this pre-release version in this early state then. Thanks for the heads-up! – poke Dec 23 '17 at 19:27
  • 2
    This was incredibly helpful to me. I'd like to add that all of this is no longed in pre-release and also there is one other key thing you have to do and that's start the bus (can't believe how long I battled with that). I just put the results of the CreateUsingRabbitMq in to a variable and called its StartAsync method (you should probably kill it at the end of the applicaiton lifetime too). – Ben Thomson Feb 16 '18 at 04:01
  • @BenThomson Glad to hear it helped! As for killing it at the end (and also starting), you could do that in a lifetime event. I’ve adjust the answer to include that part. I hope that works and won’t deadlock… xD – Since you managed to get it running, would you mind giving it a try and tell me whether that works? – poke Feb 16 '18 at 09:27
  • @poke works perfectly. I pretty much had the exact same code you put although I tweaked my start to also use the lifetime event too as I'd done it a slightly different way but yours makes more sense. – Ben Thomson Feb 17 '18 at 01:30
  • @BenThomson Glad to hear that, thanks for confirming! :) – poke Feb 17 '18 at 01:53
  • Using this approach (creating bus in `Configure` method of stratup), How will I be able to inject the bus (as `IPublishEndpoint`, `IBus` or `IBusControl`) into my controllers? – Kamyar Feb 26 '18 at 11:11
  • 1
    @Kamyar The bus won’t be available through dependency injection this way. If you need that, you should probably lift the bus construction up to the web host level of your ASP.NET Core application and register the bus at that point already. – poke Feb 26 '18 at 11:27
  • 1
    @poke: Yes I can do that. But then I have move my consumer definition up to `ConfigureServices`. Since my consumer is resolved by .NET's DI, I have to have access to `IServiceProvider` to resolve them (Which I don't have in `ConfigureServices`. – Kamyar Feb 26 '18 at 12:29
9

I also had the same issue and poke's answer didn't work for me as I needed to use DI for IBus, IPublishEndpoint and ISendEndpointProvider.

I found this example here

services.AddSingleton(provider => Bus.Factory.CreateUsingRabbitMq(cfg =>
{
   var host = cfg.Host("localhost", "/", h => { });

   cfg.ReceiveEndpoint(host, "web-service-endpoint", e =>
   {
      e.Consumer<DoSomethingConsumer>(provider);
   });
}));
miken32
  • 42,008
  • 16
  • 111
  • 154
Lloyd Powell
  • 18,270
  • 17
  • 87
  • 123
  • Wow, thank you - this solved this same annoying issue for me. I am using the MassTransit.ExtensionsDependencyInjectionIntegration and the MassTransit.AspNetCoreIntegration packages. – ewomack Jan 30 '20 at 16:30
  • Any idea if this is still relevant for the newer versions? – rumblefx0 Aug 05 '20 at 17:46
  • This also works for me. Basically my MassTransit configuration was working but when I was trying to bind a consumer with receive endpoint, it was compile-time error. – Moshi Aug 26 '20 at 04:21