Auto generated queues
Generated definitions use the named queue infrastructure. Messages remain instances of the original CLR type; Nexus no longer emits message or job subclasses. Configure RoutingQueueName to create an explicit virtual entry point for ShouldEnqueue routing, or enqueue directly into a concrete generated queue by name.
Several libraries can contribute generated queues and jobs through separate AddQueues() and AddJobs() calls. Append to the shared options collections and call Build() once after all contributions. Each generated job's concrete queue must be declared before its AddJobs() call. Definitions and shared routing entry points are registered once at build, with duplicate-name and processing-conflict validation. See library contributions.
Sometimes you have messages with the same structure but one or more discriminator properties makes them significantly different than others. Different enough that you want such message split up into multiple different queues. An example might be a message that has a Language property and you want to have different queues per language so each language can be processed individually. Or if you want to enable PauseOnError so that any error for that language pauses processing for any other message in that language.
It's possible to manually define a bunch of queues and jobs using inheritance to handle this, but it comes quite inelegant quickly because of the boilerplate needed.
Generated definitions create concrete queue registrations and job bindings from configuration, using an ordinary message contract and job implementation.
For example:
// Program.cs
[Queue("language")]
public class LanguageQueueMessage : IQueueMessage
{
public string Language { get; set; } = "";
}
public class LanguageQueueMessageJob<TMessage> : IQueueJob<TMessage>
where TMessage : LanguageQueueMessage
{
public Task<ProcessResults> ProcessMessageAsync(TMessage message, QueueProcessingContext context, CancellationToken cancellationToken)
{
return Task.FromResult(ProcessResults.Processed());
}
}
var generatedQueues = new List<IGeneratedQueue>();
var generatedJobs = new List<IGeneratedQueueJob>();
foreach (var language in GetLanguages())
{
var queueName = "language_" + language;
generatedQueues.Add(new GeneratedQueue<LanguageQueueMessage>
{
RoutingQueueName = "languages",
Name = queueName,
DisplayName = "Language queue " + language,
Category = "Language",
ShouldEnqueue = message => message.Language == language,
});
generatedJobs.Add(new GeneratedQueueJob<LanguageQueueMessage>
{
JobBaseType = typeof(LanguageQueueMessageJob<>),
Name = "language_job_" + language,
DisplayName = "Language job " + language,
Category = "Language",
QueueName = queueName,
});
}
builder.Services
.AddNexus()
.AddQueues(options =>
{
options.GeneratedQueues = generatedQueues;
})
.AddJobs(options =>
{
options.GeneratedJobs = generatedJobs;
});
With this code you you'll get one concrete queue and job per language returned from the fictional GetLanguages() method.
ShouldEnqueue is evaluated when enqueueing to the configured routing entry point, such as enqueuer.EnqueueAsync("languages", message). Every matching destination receives the original message payload through the ordinary virtual queue pipeline. Direct enqueueing to language_sv bypasses group routing.
Use an ordinary concrete job class or a concrete open generic implementation. Nexus closes generic implementations over the original message type. Abstract generated job implementations must be made concrete; no runtime derived job class is emitted. Explicit generated job names, schedules, and message filters remain part of the job binding metadata. Filters are configured expressions; parameters are not used to derive them.
Enqueueing messages
Generated destinations require explicit queue names, even when only one exists. For group routing, supply the configured virtual entry name. To obtain a queue-bound typed accessor, use IQueueResolver.GetEnqueuer<LanguageQueueMessage>("language_sv"). Type-only enqueueing does not broadcast or evaluate ShouldEnqueue.
Other queue operations
If you need any of the other services for a generated queue you'll need to request a list of the non-generic version and filter out based on queue name. So let's say that we want the Swedish queue reader from the language example at the top on this page. We'd do something like this:
var queueReaders = serviceProvider.GetServices<IQueueReader>(); // Or IEnumerable<IQueueReader> in your constructor
var language = "sv";
var swedishQueueReader = queueReaders.Single(q => q.QueueName == "language_" + language);