Kanject.Core.Queue.Provider.AwsSqs.Annotations
3.9.0
Prefix Reserved
dotnet add package Kanject.Core.Queue.Provider.AwsSqs.Annotations --version 3.9.0
NuGet\Install-Package Kanject.Core.Queue.Provider.AwsSqs.Annotations -Version 3.9.0
<PackageReference Include="Kanject.Core.Queue.Provider.AwsSqs.Annotations" Version="3.9.0" />
<PackageVersion Include="Kanject.Core.Queue.Provider.AwsSqs.Annotations" Version="3.9.0" />
<PackageReference Include="Kanject.Core.Queue.Provider.AwsSqs.Annotations" />
paket add Kanject.Core.Queue.Provider.AwsSqs.Annotations --version 3.9.0
#r "nuget: Kanject.Core.Queue.Provider.AwsSqs.Annotations, 3.9.0"
#:package Kanject.Core.Queue.Provider.AwsSqs.Annotations@3.9.0
#addin nuget:?package=Kanject.Core.Queue.Provider.AwsSqs.Annotations&version=3.9.0
#tool nuget:?package=Kanject.Core.Queue.Provider.AwsSqs.Annotations&version=3.9.0
Kanject.Core.Queue.Provider.AwsSqs.Annotations
Roslyn incremental source generators, an analyzer and a code fix for Kanject.Core.Queue.Provider.AwsSqs. The generators turn [QueueMessage], [QueueConsumer] and [RouteQueueConsumer] declarations into four kinds of output:
- DI registration methods.
- Typed send methods.
- Consumer base-class wiring.
- Lambda
SQSEventdispatch methods.
The package also writes a Markdown catalog of the project's queues at build time.
Installation
<ItemGroup>
<PackageReference Include="Kanject.Core.Queue.Provider.AwsSqs" />
<PackageReference Include="Kanject.Core.Queue.Provider.AwsSqs.Annotations" PrivateAssets="all" />
</ItemGroup>
Kanject.Core.Queue.Provider.AwsSqs already depends on this package, so the generators normally reach your project through it. Reference this package directly anyway, so the generator version is explicit and pinned alongside the provider.
This package isn't marked as a development dependency, so dotnet add package writes a plain reference. Add PrivateAssets="all" yourself so the generator doesn't flow on to projects that reference yours. If you add an IncludeAssets line to this reference, keep compile in it or delete the line. Otherwise the attribute types from Kanject.Core.Queue.Provider.AwsSqs.Annotations.Attributes can be stripped from compilation.
The package targets netstandard2.0 because it runs inside the C# compiler. The code it generates compiles against Kanject.Core.Queue.Provider.AwsSqs, which targets .NET 8, .NET 9 and .NET 10. The generators only process types declared in the project that references them.
Quick start
using Amazon.Lambda.SQSEvents;
using Kanject.Core.Queue.Abstractions.Models;
using Kanject.Core.Queue.Provider.AwsSqs.Annotations.Attributes;
namespace Shop.Orders;
[QueueMessage("orders")]
public sealed class OrderPlaced
{
public string OrderId { get; set; } = string.Empty;
}
[QueueConsumer(Message = typeof(OrderPlaced))]
[QueueConsumerDependency(typeof(IFulfilmentService))]
public partial class OrderPlacedConsumer
{
protected override async Task ConsumeAsync(
List<MessageContext<OrderPlaced>> messages, CancellationToken cancellationToken)
{
foreach (var context in messages)
{
await FulfilmentService.ShipAsync(context.Message, cancellationToken);
await AcknowledgeAsync(context);
}
}
}
With these declarations, you can write:
services
.AddAwsSqsGlobalQueueConfiguration(options => { /* credentials, region */ })
.AddOrdersQueue() // from [QueueMessage("orders")]
.AddOrderPlacedConsumer(); // from [QueueConsumer]
await queueManagerService.EnqueueOrderPlacedAsync(new OrderPlaced { OrderId = "o-1" });
// Lambda handler body:
return await serviceProvider.ProcessOrderPlacedEventAsync(sqsEvent, cancellationToken);
The generated extension classes are emitted in the namespace of the annotated type.
What gets generated
[QueueMessage("orders")] on a message type
Apply it to a class, record, record struct or struct. The queue name is converted to a method-name stem by PascalCasing its - / _ segments and appending Queue, so orders becomes OrdersQueue and order-events becomes OrderEventsQueue.
| Generated member | Purpose |
|---|---|
CreateOrdersQueue(), CreateOrdersQueue(Action<AwsSqsQueueConfiguration>, use?) |
Register the queue. The no-argument overload forces CreateQueueIfNotExist = true |
AddOrdersQueue(), AddOrdersQueue(Action<AwsSqsQueueConfiguration>, use?) |
Register the queue using the global configuration, optionally adjusted |
EnqueueOrderPlacedAsync(message), (message, metadata), (IList<OrderPlaced>), (IList<(OrderPlaced message, Dictionary<string, string> metadata)>) |
Typed sends on IQueueManagerService |
DequeueOrderPlacedAsync(), AcknowledgeOrderPlacedAsync(MessageContext<OrderPlaced>) |
Typed receive and acknowledge on IQueueManagerService |
A cluster node on the attribute ([QueueMessage("orders", 2)]) is sent as the cNode metadata entry by every Enqueue…Async overload, single or batch, on each message. Metadata you pass in is copied, not modified, and a cNode entry you supply yourself is kept.
[QueueConsumer] on a partial class
| Property | Meaning |
|---|---|
Message |
Message type. When set, the class derives from AbstractQueueConsumer<TMessage> and overrides ConsumeAsync(List<MessageContext<TMessage>>, CancellationToken). When omitted, it derives from AbstractRawQueueConsumer and overrides ConsumeAsync(List<Message>, CancellationToken) |
QueueName |
Queue name. Falls back to the Message type's [QueueMessage] name, then to the class name (without a trailing Message) |
QueueNamespace |
Optional queue namespace |
The generator writes the base class and a constructor that takes IQueueRegistry plus one parameter per [QueueConsumerDependency(typeof(T))]. Each dependency is also exposed as a protected get-only property. An interface loses its leading I, so IFulfilmentService becomes FulfilmentService, and the optional second argument overrides the name. Don't declare a base class or constructor yourself.
For a class named OrderPlacedConsumer, it also generates:
| Generated member | Purpose |
|---|---|
AddOrderPlacedConsumer(), AddOrderPlacedConsumer(Action<AwsSqsQueueConfiguration>, use?) |
Register the consumer as a singleton and hosted service |
ProcessOrderPlacedEventAsync(SQSEvent, CancellationToken = default) |
Lambda entry point on IServiceProvider that returns SQSBatchResponse (a trailing Consumer is dropped from the name) |
AddOrderPlacedConsumerWatcher() |
On IServiceProvider: starts the consumer's polling loop manually |
[QueueConsumerFilter]: several consumers on one queue
When the [QueueConsumer] classes on one queue carry a [QueueConsumerFilter] on the same message property, the generator adds one dispatcher for the queue. For orders, that's ProcessOrdersQueueEventAsync(SQSEvent, CancellationToken = default):
// OrderEvent carries [QueueMessage("orders")]
[QueueConsumer(Message = typeof(OrderEvent))]
[QueueConsumerFilter("Kind", new[] { "created", "updated" })]
public partial class OrderChangedConsumer { /* ConsumeAsync */ }
[QueueConsumer(Message = typeof(OrderEvent))]
[QueueConsumerFilter("Kind", new[] { "cancelled" })]
public partial class OrderCancelledConsumer { /* ConsumeAsync */ }
The dispatcher reads the property from each record's JSON body, either at the top level or inside an SNS-style Message envelope. It matches values case-insensitively and passes each group of records to its consumer, then returns the combined failures, or null when nothing failed.
The enum form is [QueueConsumerFilter<OrderKind>("Kind", new[] { OrderKind.Cancelled }, EnumComparisonMode.Name)]. EnumComparisonMode.Name compares member names (for JsonStringEnumConverter), and the default EnumComparisonMode.Value compares numeric values. All consumers that filter the same property must use the same form and mode.
A record whose property is missing or has an unlisted value makes the dispatcher throw InvalidOperationException, which fails the whole invocation.
[RouteQueueConsumer] on a partial class
[RouteQueueConsumer] takes the same QueueName / QueueNamespace / Message properties. Add one or more [QueueConsumerRoute("route")] attributes. The class derives from AbstractRouteQueueConsumer<TMessage>, or AbstractRouteRawQueueConsumer when Message is omitted, and gets the same dependency-injecting constructor. Route consumers are registered as scoped services.
For every queue that has route consumers (for example order-events), the generator adds:
| Generated member | Purpose |
|---|---|
AddOrderEventsQueueQueueRouter(Action<(AwsSqsQueueConfiguration, IServiceCollection)>? = null) |
Registers the queue and maps every declared route to its consumer |
RouteSqsEventToOrderEventsQueueAsync(SQSEvent, CancellationToken = default) |
Lambda entry point that groups records by their QueueRoute message attribute and dispatches each group. Records without the attribute go to the default route; records that reach no consumer are returned in BatchItemFailures |
Publish to a route with queueManagerService["order-events"].EnqueueAsync("order.created", message).
Queue schema file
After each build, the package's MSBuild target writes $(AssemblyName).queueschema.md next to the project file. The file is a Markdown catalog of the project's queue messages, consumers, route consumers and subscriptions, which is useful as reference or AI-prompt context. dotnet clean deletes it.
| MSBuild property | Default | Effect |
|---|---|---|
GenerateQueueSchema |
true |
Set to false to skip the file |
QueueSchemaOutputDir |
(project directory) | Relative (to the project) or absolute output directory |
While GenerateQueueSchema is on, the target also sets EmitCompilerGeneratedFiles=true unless you set it yourself, because it reads the generated source from disk.
Event-hub integration attributes
[EventQueue], [SubscribeToQueue] and [EventHubQueueRouter] are hooks for event-hub integrations that this SDK doesn't ship:
[EventQueue]generatesCreate…EventQueue/Acknowledge…helpers only when the same type also carries a[HubEvent]or[EventHubTopic]attribute.[SubscribeToQueue]is recorded in the queue schema only.[EventHubQueueRouter]on a route consumer suppresses the generated router methods for it.
Diagnostics
| ID | Severity | What it means |
|---|---|---|
TRF001 |
Error | A [RouteQueueConsumer] class has no [QueueConsumerRoute]. A code fix adds one |
TRF002 |
Error | Two [RouteQueueConsumer] classes register the same route on the same queue. The second would be ignored at runtime |
KANQUE001 |
Error | [QueueConsumerFilter] consumers of one queue filter on different properties, so no dispatcher can be generated |
The generators also report KANJECTGEN002 (error) in three cases:
- Two consumers of one queue declare the same
[QueueConsumerFilter]value. - Consumers that filter the same property mix enum comparison modes.
- A
[QueueConsumerFilter]is placed on a route consumer, which isn't supported.
Related packages
| Package | Role | Availability |
|---|---|---|
Kanject.Core.Queue.Provider.AwsSqs |
The runtime the generated code targets | nuget.org |
Kanject.Core.Queue.Provider.AwsSqs.Annotations.Attributes |
The attribute types, a dependency of this package | nuget.org |
Kanject.Core.Queue.Provider.AwsSqs.Abstractions |
SQS contracts and Lambda handler helpers used by generated code | nuget.org |
Kanject.Core.Queue.Abstractions |
Provider-neutral queue contracts | nuget.org |
Kanject.Core.Queue.Provider.AwsSqs.Extensions.EventBridge.Annotations |
Generator for typed EventBridge Scheduler helpers | Commercial license (not on nuget.org) |
License
Licensed under the Kanject Code Libraries License Agreement (KCLLA); the full text ships in this package as LICENSE.md. Organizations whose trailing-twelve-month gross revenue and total funding raised are each below US$250,000 may use it at no cost under the Free Tier. At or above either threshold a commercial license is required — contact commercial@kanjectbusiness.com.
Learn more about Target Frameworks and .NET Standard.
-
.NETStandard 2.0
- Kanject.Core.Annotations (>= 3.14.0)
- Kanject.Core.Queue.Provider.AwsSqs.Annotations.Attributes (>= 3.8.1)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Kanject.Core.Queue.Provider.AwsSqs.Annotations:
| Package | Downloads |
|---|---|
|
Kanject.Core.Queue.Provider.AwsSqs
Kanject Core Queue Provider AWS Sqs |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.9.0 | 38 | 10/2/2026 |
| 3.8.1 | 126 | 9/27/2026 |
| 3.8.0 | 92 | 9/27/2026 |
| 3.7.7 | 103 | 9/26/2026 |
| 3.7.6 | 134 | 9/7/2026 |
| 3.7.5 | 115 | 8/27/2026 |
| 3.7.4 | 128 | 8/22/2026 |
| 3.7.3 | 128 | 8/10/2026 |
| 3.7.2 | 129 | 8/9/2026 |
| 3.7.1 | 116 | 8/5/2026 |
| 3.7.0 | 122 | 8/5/2026 |
| 3.6.0 | 135 | 8/3/2026 |
| 3.5.5 | 137 | 7/30/2026 |
| 3.5.4 | 147 | 7/18/2026 |
| 3.5.3 | 160 | 7/13/2026 |
| 3.5.2 | 120 | 7/11/2026 |
| 3.5.1 | 139 | 7/11/2026 |
| 3.5.0 | 159 | 7/9/2026 |
| 3.4.1 | 133 | 7/9/2026 |