I want to make my API both publish events (outside any incoming message handlers, e.g. from an action method) and subscribe to events. Publishing events works. I used the older versions of Program
and Startup
to integrate using GenericHost
and an IMessageSession
is sucessfully injected into the constructor.
However, when the commented-out code is activated to make the API subscribe to and handle events, suddenly there is an exception about injecting IMessageSession
being thrown when initializing the host and running the application.
TestController.cs
namespace TestContext
{
[Route("api/[controller]")]
[ApiController]
public class TestController : ControllerBase/*, IHandleMessages<ReceivedEvent>*/
{
private IMessageSession _session;
public TestController(IMessageSession session)
{
_session = session;
}
/*
[NonAction]
public Task Handle(ReceivedEvent message, IMessageHandlerContext context)
{
throw new NotImplementedException();
}*/
[HttpPost]
public async Task<ActionResult> Test()
{
await _session.Publish(new PublishedEvent());
return Ok();
}
}
}
Program.cs
public class Program
{
public static void Main(string[] args)
{
CreateHostBuilder(args).Build().Run();
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseNServiceBus(context =>
{
var endpointConfiguration = new EndpointConfiguration("Test");
var transport = endpointConfiguration.UseTransport<LearningTransport>();
return endpointConfiguration;
})
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Exception
Why is the exception being thrown and how to make the API subscribe to and handle events?
EDIT
When handling the event from a separate service class, the exception is not thrown.
public class HandlerService : IHandleMessages<ReceivedEvent>
{
public Task Handle(ReceivedEvent message, IMessageHandlerContext context)
{
Console.WriteLine(message);
return Task.CompletedTask;
}
}
The exception message is actually pretty good here. Most of the NServiceBus exception messages are.
Interfaces IMessageSession or IEndpointInstance should not be resolved from the container to enable sending or publishing messages from within sagas or message handlers. Instead, use the context parameter on the TestController.Handle method to send or publish messages.
So first, don't mix a handler in a controller. It needs to be a separate class.
Then, don't inject IMessageSession. That interface is explicitly for things like controller actions where you don't have an existing "a message is being handled right now" and you want to send a message.
Instead, if you want to send or publish within the handler, use the context:
public class MyHandler : IHandleMessages<ReceivedEvent>
{
private IMessageSession session;
public MyHandler(IMessageSession session)
{
// Won't work! Don't inject IMessageSession!
this.session = session;
}
public async Task Handle(ReceivedEvent message, IMessageHandlerContext context)
{
// NOPE!
session
}
}
public class MyHandler : IHandleMessages<ReceivedEvent>
{
public async Task Handle(ReceivedEvent message, IMessageHandlerContext context)
{
context.Send(new CommandToSend());
context.Publish(new EventToPublish());
}
}