| id | dependency-injection |
|---|---|
| title | Dependency Injection |
| sidebar_label | Dependency Injection |
ArtemisNetClient.Extensions.DependencyInjection integrates ArtemisNetClient with Microsoft.Extensions.DependencyInjection. For ASP.NET Core applications, it is the easiest way to register long-lived connections, consumers, producers, and request-reply clients.
For DI-only registration:
dotnet add package ArtemisNetClient.Extensions.DependencyInjectionFor ASP.NET Core or generic hosted applications, add the hosting package as well:
dotnet add package ArtemisNetClient.Extensions.HostingThe extension methods live in these namespaces:
using ActiveMQ.Artemis.Client;
using ActiveMQ.Artemis.Client.Extensions.DependencyInjection;
using ActiveMQ.Artemis.Client.Extensions.Hosting;AddActiveMq registers the connection and the ArtemisNetClient services in the DI container. AddActiveMqHostedService starts those registrations when the host starts.
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddActiveMqHostedService();
var endpoint = Endpoint.Create(
host: "localhost",
port: 5672,
user: "artemis",
password: "artemis");
var activeMq = builder.Services.AddActiveMq(
name: "my-artemis",
endpoints: new[] { endpoint });
activeMq.AddConsumer(
address: "orders.incoming",
routingType: RoutingType.Anycast,
handler: async (message, consumer, serviceProvider, cancellationToken) =>
{
var publisher = serviceProvider.GetRequiredService<OrderEventsProducer>();
var orderId = message.GetBody<string>();
await publisher.PublishAccepted(orderId, cancellationToken);
await consumer.AcceptAsync(message);
});
activeMq.AddProducer<OrderEventsProducer>("orders.events", RoutingType.Multicast);
var app = builder.Build();
app.Run();
public sealed class OrderEventsProducer
{
private readonly IProducer _producer;
public OrderEventsProducer(IProducer producer) => _producer = producer;
public Task PublishAccepted(string orderId, CancellationToken cancellationToken)
{
return _producer.SendAsync(new Message($"accepted:{orderId}"), cancellationToken);
}
}:::important
If you register ArtemisNetClient services with AddActiveMq but do not start them with AddActiveMqHostedService, your consumers and typed producers will not be started automatically.
This split is intentional. Hosted applications usually want AddActiveMqHostedService, while advanced scenarios can control startup manually through IActiveMqClient.
:::
AddActiveMq returns an IActiveMqBuilder. Use that builder to compose the messaging setup for one logical connection:
ConfigureConnectionFactorycustomizesConnectionFactory.ConfigureConnectionattaches runtime hooks to the createdIConnection.AddConsumerregisters message handlers.AddProducerregisters typed producers backed byIProducer.AddAnonymousProducerregisters typed producers backed byIAnonymousProducer.AddRequestReplyClientregisters typed request-reply clients.EnableQueueDeclarationandEnableAddressDeclarationenable topology declaration on startup.
The name parameter identifies the logical connection registration. Use different names when you need multiple Artemis connections in the same application.
The simplest consumer attaches to an address and routing type:
activeMq.AddConsumer(
address: "orders.incoming",
routingType: RoutingType.Anycast,
handler: async (message, consumer, serviceProvider, cancellationToken) =>
{
var logger = serviceProvider.GetRequiredService<ILogger<Program>>();
logger.LogInformation("Received order {OrderId}", message.GetBody<string>());
await consumer.AcceptAsync(message);
});The handler signature is:
Func<Message, IConsumer, IServiceProvider, CancellationToken, Task>That gives you direct access to the incoming Message, the active IConsumer, the current IServiceProvider, and the shutdown CancellationToken.
Use the other overloads when you need more control:
- Add
ConsumerOptionsto configure credit, concurrent consumers, filters, orNoLocalbehavior. - Add a queue name when the consumer should bind to a specific queue.
- Add
QueueOptionswhen queue declaration is enabled and queue settings need to be declared on startup. - Use
AddSharedConsumerorAddSharedDurableConsumerfor shared subscription patterns.
Typed producers keep your application code away from raw Artemis primitives while still reusing a long-lived underlying IProducer.
activeMq.AddProducer<OrderEventsProducer>("orders.events", RoutingType.Multicast);
public sealed class OrderEventsProducer
{
private readonly IProducer _producer;
public OrderEventsProducer(IProducer producer) => _producer = producer;
public Task PublishAccepted(string orderId, CancellationToken cancellationToken)
{
return _producer.SendAsync(new Message($"accepted:{orderId}"), cancellationToken);
}
}AddProducer<TProducer> registers TProducer as a typed wrapper. By default the wrapper type is transient, but you can choose Singleton or Scoped when that better matches your application.
Each typed wrapper type must be unique per registration. If you need two producers with the same public API, create two distinct wrapper classes.
Use AddAnonymousProducer<TProducer> when the producer must publish to multiple addresses dynamically.
activeMq.AddAnonymousProducer<ReplyProducer>();
public sealed class ReplyProducer
{
private readonly IAnonymousProducer _producer;
public ReplyProducer(IAnonymousProducer producer) => _producer = producer;
public Task SendAsync(string address, Message message, CancellationToken cancellationToken)
{
return _producer.SendAsync(address, message, cancellationToken);
}
}AddRequestReplyClient<TClient> registers a typed wrapper over IRequestReplyClient.
activeMq.AddRequestReplyClient<BookstoreRequestClient>();
public sealed class BookstoreRequestClient
{
private readonly IRequestReplyClient _client;
public BookstoreRequestClient(IRequestReplyClient client) => _client = client;
public Task<Message> SendAsync(string address, string payload, CancellationToken cancellationToken)
{
return _client.SendAsync(
address,
RoutingType.Anycast,
new Message(payload),
cancellationToken);
}
}On the response side, handle the request with a consumer and reply to message.ReplyTo. The response must preserve message.CorrelationId.
activeMq.AddAnonymousProducer<ReplyProducer>();
activeMq.AddConsumer(
address: "bookstore.requests",
routingType: RoutingType.Anycast,
handler: async (message, consumer, serviceProvider, cancellationToken) =>
{
var replyProducer = serviceProvider.GetRequiredService<ReplyProducer>();
await replyProducer.SendAsync(
message.ReplyTo,
new Message("ok")
{
CorrelationId = message.CorrelationId
},
cancellationToken);
await consumer.AcceptAsync(message);
});Use ConfigureConnectionFactory for transport, logging, recovery, or message ID settings.
activeMq.ConfigureConnectionFactory((serviceProvider, factory) =>
{
factory.LoggerFactory = serviceProvider.GetRequiredService<ILoggerFactory>();
factory.AutomaticRecoveryEnabled = true;
});Use ConfigureConnection for events on the created IConnection:
activeMq.ConfigureConnection((_, connection) =>
{
connection.ConnectionClosed += (_, args) =>
{
Console.WriteLine($"Connection closed. Error={args.Error}");
};
});If the application should declare queues or addresses when it starts, enable topology declaration:
activeMq
.AddConsumer(
address: "orders.incoming",
routingType: RoutingType.Anycast,
queue: "orders-service",
handler: async (message, consumer, serviceProvider, cancellationToken) =>
{
await consumer.AcceptAsync(message);
})
.EnableQueueDeclaration()
.EnableAddressDeclaration();QueueOptions and ConfigureTopology let you refine the declared topology when startup needs to create or update broker objects.
Each AddActiveMq(name, endpoints) call registers one logical connection. Use different names when your application needs separate clusters or credentials.
Keep the registrations for one connection on the returned builder so the consumers, producers, topology configuration, and observers all stay attached to the intended connection.
Hosted applications typically use AddActiveMqHostedService, but the DI package also registers IActiveMqClient for manual lifecycle control.
That is useful when you need to start messaging later in the application lifecycle or coordinate startup with other infrastructure.