Skip to content

Producer

blehnen edited this page Nov 10, 2026 · 9 revisions

Producing messages

The message can be any serializable class.

Creating the queue

You must specify the transport that you want to use.

var queueConnection = new QueueConnection(queueName, connectionString);
using (var queueContainer = new QueueContainer<InitModule>())
{
	using (var queue = queueContainer.CreateProducer<YourMessageClass>(queueConnection))
	{
		...
	}
}

If you dispose of the container, it also disposes all queues that it created. If you send many messages over time, you should keep the container and queues instead of creating new ones each time.

The send commands are thread safe.

You can send messages in a few different ways.

A single message with no additional message metadata
var result = queue.Send(new SimpleMessage {Message = "Hello World"});
A single message with additional metadata, such as a message delay
var data = new AdditionalMessageData();
data.SetDelay(TimeSpan.FromMinutes(1));
var result = queue.Send(new SimpleMessage {Message = "Hello World"}, data);
A collection of messages, with or without additional metadata
var result = queue.Send(messageList);

The batch is all-or-nothing on most transports. SQL Server, SQLite, PostgreSQL and LiteDb send the whole batch in one connection and one transaction. Any failure rolls back every message in the batch, and each returned IQueueOutputMessage reports the failure. If you need one bad message to fail on its own while the rest commit, call Send(message) in a loop instead.

There are two exceptions:

  • Memory sends the batch as an ordered loop rather than as a single unit
  • Redis sends it as one Lua script, which Redis executes atomically

On every transport, the results come back in the order that you supplied the messages. You can use the position to match a generated id to the message it belongs to.

Scheduled jobs are not supported on the batch path. The batch path rejects them with a NotSupportedException. Send scheduled jobs one at a time. The "is this job already queued?" check is a check-then-act, and the batch path deliberately does not take a lock for it.

SQL Server, SQLite and PostgreSQL have a BatchSize transport option. It is an optional ceiling on the chunk size. The value is clamped down to a safe maximum that comes from the transport's own limits. A value of 0 means "use that maximum". You do not normally need to set it.

A large batch holds a single write transaction until it commits. On SQLite this matters more than on other transports. Under the default WAL journal mode, readers continue during the transaction. But SQLite still serializes writers, so another writer can wait behind a large batch.

To send a batch inside a transaction that you own, see Outbox Pattern. That path reports failures in a different way: it throws, so you can roll back.

All of the above commands have async methods as well
var result = await queue.SendAsync(new SimpleMessage {Message = "Hello World"});

All methods return the result of the operation. This includes the following:

  • A boolean indicating if an error occurred
  • An exception containing the error, if an error occurred.
  • Information about the sent message, assuming no errors -The message ID -The correlation ID

Methods that take a collection of messages return a collection of results. The result collection has a boolean named 'HasErrors' that shows if any result failed.

Clone this wiki locally