Azure Service Bus for messaging and saga persistence - c#

I'm trying to persist the states of my saga into the Session of the AzureService Bus Messages. As long as the State of the Sage is just in memory it works fine. But if I activate RequiresSession and use a MessageSessionSagaRepository, my sage isn't doing anything anymore.
services.AddMassTransit(x =>
{
x.AddBus(provider => Bus.Factory.CreateUsingAzureServiceBus(cfg =>
{
cfg.Host(ConnectionString);
cfg.ReceiveEndpoint(EnrichQueue, e =>
{
// works
//e.StateMachineSaga(new EnrichmentStateMachine(), new InMemorySagaRepository<EnrichmentState>());
// doesn't work
e.RequiresSession = true;
e.StateMachineSaga(new EnrichmentStateMachine(), MessageSessionSagaRepository.Create<EnrichmentState>());
});
}));
});
Did I miss something that I should have configured?

Your configuration is a bit mixed, I'd suggest cleaning it up to bring it up to date and make it consistent:
services.AddMassTransit(x =>
{
x.AddSagaStateMachine<EnrichmentStateMachine, EnrichmentState>()
.MessageSessionRepository();
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host(ConnectionString);
cfg.ReceiveEndpoint(EnrichQueue, e =>
{
e.RequiresSession = true;
e.ConfigureSaga<EnrichmentState>(context);
});
}));
});
Messages sent/published to that endpoint require a SessionId, which is covered in the documentation.

Related

Consume method is not being called using MassTransit with Azure Service BusTopics in a .Net microservice application

I know that this is being asked a lot, but I already visited a lot of question and I'm in the dark at the moment.
I'm using 2 different apps, one is the producer and the other one is the consumer. The applications are using topic-subscription type.
Producer:
services.AddMassTransit(serviceCollectionConfigurator =>
{
serviceCollectionConfigurator.AddBus(registrationContext => Bus.Factory.CreateUsingAzureServiceBus(configurator =>
{
configurator.Host("connectionString");
configurator.Message<CreateEvent>(configTopology =>
{
configTopology.SetEntityName("topic1");
});
}
));
});
Consumer:
services.AddMassTransit(x =>
{
x.AddConsumer<CreateConsumer>(); //Consumer class
x.UsingAzureServiceBus((context, cfg) =>
{
cfg.Host("connectionString");
cfg.Message<CreateEvent>(configTopology =>
{
configTopology.SetEntityName("topic1");
});
cfg.SubscriptionEndpoint<CreateEvent>("subscription1", e =>
{
e.PrefetchCount = 100;
e.MaxConcurrentCalls = 100;
e.LockDuration = TimeSpan.FromMinutes(5);
e.MaxAutoRenewDuration = TimeSpan.FromMinutes(30);
e.UseMessageRetry(r => r.Intervals(100, 200, 500, 800, 1000));
e.UseInMemoryOutbox();
e.ConfigureConsumer<CreateConsumer>(context);
});
});
});
}
public class CreateConsumer: IConsumer<CreateEvent>
{
public Task Consume(ConsumeContext<CreateEvent> context)
{
//breakpoint here
}
}
One of the errors I spotted was the namespace of CreateEvent needs to be the same. When the producer publish the message I can't see the message being sent to azure, neither being received in the consumer.
I assume the problem can be located in both configurations but I have no clue. Any thoughts?
Edit: Forgot to mention I'm using masstransit v8, it seems by default they inject the hosted service to run the bus automatically.

Masstransit not creating exchange

Good day
I'm solving the problem that exchange is not being automatically created
I have registered Masstransit in net6 net core application using such uri opions (have tried both):
rabbitmq://myurl
rabbitmq://myurl:5672
Registration looks like this:
services.AddMassTransit(mt =>
{
mt.UsingRabbitMq((context, cfg) =>
{
cfg.Host(new Uri(
RabbitMqOptions.RabbitMqUri),
RabbitMqOptions.VHost,
credentials =>
{
credentials.Username(RabbitMqOptions.UserName);
credentials.Password(RabbitMqOptions.Password);
});
cfg.AutoStart = true;
cfg.Publish<IServerNotificationMessage>(e => e.ExchangeType = RabbitMQ.Client.ExchangeType.Direct);
});
});
services.AddMassTransitHostedService();
Debugging publishing code shows that actual port used is 0 and bus control is null and not started
see the print screen
How can I make the bus start? (as I understand cfg.Host returns void, rather than buscontrol, so that it cannot be explicitly started, have specified autostart option, though its still down)
Thank you in advance
A URI is not required to configure MassTransit, you might just simplify your configuration as shown below.
services.AddMassTransit(mt =>
{
mt.UsingRabbitMq((context, cfg) =>
{
cfg.Host(RabbitMqOptions.Host,
RabbitMqOptions.Port,
RabbitMqOptions.VHost,
h =>
{
h.Username(RabbitMqOptions.UserName);
h.Password(RabbitMqOptions.Password);
});
cfg.AutoStart = true;
cfg.Publish<IServerNotificationMessage>(e => e.ExchangeType = RabbitMQ.Client.ExchangeType.Direct);
});
});
services.AddMassTransitHostedService();
The logs should show the bus starting, if they don't, then the hosted service is not being started. Is this an ASP.NET project, or a project using the .NET Generic Host?

How can I add publish message configuration on a running IBusControl

I'm using MassTransit with RabbitMqTransport.
Assume I have run IBusControl using:
var control = Bus.Factory.CreateUsingRabbitMq(c =>
{
var host = confgurator.Host(config.BuildHostUri(), h =>
{
...
});
...
});
await control.StartAsync();
Later I connected new endpoint to this running instance, using:
host.ConnectReceiveEndpoint(Configuration.QueueName, this.ConfigureEndpoint);
Is there a way to configure Publish/Send for new Message types at this moment also? By "configure Publish/Send" I mean using methods like existing on IRabbitMqBusFactoryConfigurator:
confgurator.Send<MessageContract>(_ =>
{
_.UseRoutingKeyFormatter(__ => Configuration.QueueName);
});
confgurator.Message<MessageContract>(x => x.SetEntityName(nameof(MessageContract)));
confgurator.Publish<MessageContract>(_ =>
{
...
}
As per Chris Patterson comment, configuring Publish/Send for message type can only be done during configuration, prior to starting the bus.

MassTransit: Adding Headers to Publish Pipeline

I'm using MassTransit 3.2.4 and I'm trying to add some header information for to my published messages but the code to set the header never seems to run. I'm not sure why this doesn't work.
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost/"), h {});
config.ReceiveEndpoint(host, "TestPublisher", e =>
{
e.ConfigurePublish(x => x.UseSendExecute(context =>
context.Headers.Set("HeaderKey", "HeaderValue")
));
});
});
On the consumer end I'm trying to read the header
public Task Consume(ConsumeContext<IActionHappened> context)
{
var headerValue = context.Headers.Get("HeaderKey", "Default Value");
}
Do I need to add an interceptor or something else in order to set header information?
Figured it out after much guessing. Just had the ConfigurePublish in the wrong place
var bus = Bus.Factory.CreateUsingRabbitMq(config =>
{
var host = config.Host(new Uri("rabbitmq://localhost/"), h => {});
config.ConfigurePublish(x => x.UseSendExecute(context =>
{
context.Headers.Set("HeaderKey", "HeaderValue");
}));
}

Integration testing microservices using MassTransit

I am trying to create black box integration tests around some services that I have created using MassTransit. The gist of the service is that it receives a message on a MassTransit channel, processes the message and sends the message on a different MassTransit channel (note that this is not a Request-Reply semantic but more of a component in a pipeline):
IBusControl bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
{
var host = cfg.Host(new Uri("rabbitmq://localhost"),
h =>
{
h.Username("guest");
h.Password("guest");
});
cfg.ReceiveEndpoint(host, "Queue1", ep =>
{
ep.Handler<ItemStarted>(context =>
{
ItemFinished item = FinishMessage(context.Message);
context.Publish(item);
});
});
});
bus.Start();
In order to test this I think what I need is a synchronous way to receive messages from the bus. Is there any way to do this:
IBusControl bus = Bus.Factory.CreateUsingRabbitMq(cfg =>
var host = cfg.Host(new Uri("rabbitmq://localhost"),
h =>
{
h.Username("guest");
h.Password("guest");
}));
bus.Start();
bus.Publish(new ItemStarted());
// This is what does not seem to exist
ItemFinished finished = bus.Receive<ItemFinished>(timeout : Timespan.FromSeconds(5));
// Assertions about finished
Is there a way to receive messages directly from MassTransit without wiring up a consumer class or lambda?
There is a MultiTestConsumer that can be used to receive and track messages that are produced during testing. You can see the class here:
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit/Testing/MultiTestConsumer.cs
And you can see how it is used here:
https://github.com/MassTransit/MassTransit/blob/develop/src/MassTransit.Tests/MultiTestConsumer_Specs.cs
It can also be used as a regular subscribed consumer, which sets up bindings in RabbitMQ to receive published messages.
I would recommend checking out the TestFramework, as there are a bunch of asynchronous testing helpers in there.

Categories