Kanject.Core.Annotations
3.14.0
Prefix Reserved
dotnet add package Kanject.Core.Annotations --version 3.14.0
NuGet\Install-Package Kanject.Core.Annotations -Version 3.14.0
<PackageReference Include="Kanject.Core.Annotations" Version="3.14.0" />
<PackageVersion Include="Kanject.Core.Annotations" Version="3.14.0" />
<PackageReference Include="Kanject.Core.Annotations" />
paket add Kanject.Core.Annotations --version 3.14.0
#r "nuget: Kanject.Core.Annotations, 3.14.0"
#:package Kanject.Core.Annotations@3.14.0
#addin nuget:?package=Kanject.Core.Annotations&version=3.14.0
#tool nuget:?package=Kanject.Core.Annotations&version=3.14.0
Kanject.Core.Annotations
Roslyn incremental source generators and analyzers for the attribute-driven features of Kanject.Core: [Parallel] fan-out, [Recurring] / [RecurringHosted] schedules, [QueryableEnum] lookups, and the opt-in PrintInConsole interceptors. The generators turn attributes into ordinary C# at compile time, so these features use no runtime reflection. The analyzers catch invalid attribute usage in the editor, before the generator would otherwise produce nothing or broken code.
Kanject.Core depends on this package, so its generators normally reach your project through Kanject.Core. Reference it explicitly anyway, on the same version as Kanject.Core, so the generator version is pinned alongside the runtime.
Installation
<ItemGroup>
<PackageReference Include="Kanject.Core" />
<PackageReference Include="Kanject.Core.Annotations" PrivateAssets="all" />
</ItemGroup>
Or from the CLI: dotnet add package Kanject.Core.Annotations, then add PrivateAssets="all" to the new reference. Keep its version equal to your Kanject.Core version, because the generated code calls into Kanject.Core's runtime types.
You need this package for the following:
[Parallel]generated overloads[Recurring]/[RecurringHosted]generated loops, services and registrations[QueryableEnum]on your own enums- the
PrintInConsoleinterceptors and the MSBuild properties - every diagnostic in the table below
You don't need it for the parts of Kanject.Core you call directly, such as ParallelLoop, RecurringLoop, the timezone, JSON and collection helpers, or plain PrintInConsole.
Package layout. This is not a development-dependency package. It ships the generators in analyzers/dotnet/cs, a lib/netstandard2.0 assembly, MSBuild props and targets in build/ and buildTransitive/, and a dependency on Kanject.Core.Annotations.Attributes (the attribute types). PrivateAssets="all" stops the generators from also running in projects that reference yours.
dotnet add package writes a plain reference for this package, with no IncludeAssets line. If you add an IncludeAssets line yourself, make sure it includes compile. The attribute types from Kanject.Core.Annotations.Attributes reach your project through this package, and an IncludeAssets list without compile strips them from compilation.
The analyzer assembly targets netstandard2.0, as Roslyn requires. The generated code calls into Kanject.Core and targets the same frameworks: .NET 8, .NET 9 and .NET 10.
Quick start
using Kanject.Core.Annotations.Attributes.Recurring;
using Kanject.Core.Annotations.Attributes.Recurring.Enums;
namespace Shop;
public sealed partial class HeartbeatService(IPingClient client)
{
[Recurring(5, RecurringRateUnit.Seconds, MaxIterations = 12)]
public Task PingAsync(CancellationToken ct) => client.PingAsync(ct);
}
At compile time, RecurringGenerator emits HeartbeatServiceRecurringExtensions.PingAsyncRecurringAsync(...), a partial IHeartbeatServiceRecurring interface, and a partial declaration of HeartbeatService that implements it. Callers write await heartbeat.PingAsyncRecurringAsync(cancellationToken: ct). If the method returned an unsupported type, or were async void, the analyzer would report KANREC001 or KANREC012 instead of leaving you with no extension method.
What it generates
| Generator | Triggered by | Emits |
|---|---|---|
ParallelGenerator |
[Parallel] on a method |
{Type}ParallelExtensions with {Method}ParallelAsync (over ReadOnlyMemory<T>, T[], IReadOnlyList<T>, IEnumerable<T>, IAsyncEnumerable<T>), {Method}ParallelStream (chunk methods that return a value), and {Method}ParallelOutcomesAsync (per-item methods that return a value); plus a partial I{Type}Parallel interface and a partial class that implements it |
RecurringGenerator |
[Recurring] on a method |
{Type}RecurringExtensions with {Method}RecurringAsync, plus {Method}RecurringStream and {Method}RecurringToLastAsync for methods that return a value; plus a partial I{Type}Recurring interface and a partial class that implements it |
RecurringHostedGenerator |
[RecurringHosted] together with [Recurring] |
{Type}{Method}RecurringHostedService : BackgroundService and {Type}RecurringHostedExtensions.Add{Type}{Method}Recurring(...) with overloads taking nothing, an Action<RecurringOptions>, or an IConfiguration |
QueryableEnumGenerator |
[QueryableEnum] on an enum |
A {Enum}Info record struct and a static {Enum}Query class: name/description/display-name lookups, Get{Parameter} accessors for each [EnumParameter], parsing and search helpers, and one nested const string class per entry in ConstantParameters |
PrintInConsoleInterceptorGenerator |
<EnablePrintInConsoleInterceptor>true</EnablePrintInConsoleInterceptor> |
C# interceptors for every PrintInConsole call. Each one carries a severity inferred at compile time and routes through ConsolePrintInterceptorProvider |
Generated member names append a fixed suffix to the method name. [Parallel] drops a trailing Async first (ProcessAsync becomes ProcessParallelAsync). [Recurring] keeps the name as written (PingAsync becomes PingAsyncRecurringAsync, and AddHeartbeatServicePingAsyncRecurring for hosted registration).
Things to know when annotating code:
- Instance methods need a
partialcontaining type. The Parallel and Recurring generators add apartialdeclaration of the same kind (class, record or struct) to the containing type so it implements the generated interface. The generated extension class and interface areinternalwhen the containing type isn't public.KANPAR011([Parallel]) andKANREC013([Recurring]) report a missingpartial. [QueryableEnum]output follows the enum. It is emitted into the enum's namespace (or the global namespace) and names the enum by its fully qualified name, so nested enums work. It isinternalwhen the enum isn't public. Aprivateorprotectednested enum can't be reached from the generated top-level types and reportsKANQE001. Aliased members (A = 1, B = 1) share one value entry, owned by the first-declared name.- Some shapes are not generated. Generic methods (
KANPAR006,KANREC004) and methods on nested types (KANPAR007,KANREC003) get a warning instead of generated code.ref/out/inparameters are errors (KANPAR008,KANREC008). [ParallelHosted]is validated but not generated. The analyzer checks it (KANPAR009,KANPAR010) and reportsKANPAR014because this release emits no hosting code for it.
MSBuild properties
Set these in the project file of a project that references this package. The package exposes them to the generators and analyzers as CompilerVisibleProperty items.
| Property | Default | Effect |
|---|---|---|
EnablePrintInConsoleInterceptor |
off | Emits the PrintInConsole interceptors and adds Kanject.Core.SystemConsole.Interceptors to InterceptorsNamespaces / InterceptorsPreviewNamespaces |
EnableParallelGenerator |
on | false disables [Parallel] generation |
EnableRecurringGenerator |
on | false disables [Recurring] generation |
EnableRecurringHostedGenerator |
on | false disables [RecurringHosted] generation while keeping [Recurring] |
EnableKanjectDevAnalyzers |
off | Turns on the file-organization analyzers KANDEV001-KANDEV005 (see below) |
KanjectDeterministicCode |
off | Turns on the determinism analyzer KANDEV006 |
Opt-in convention analyzers
The package also contains optional analyzers for a strict file-organization convention: one top-level type per file, no accessible nested types, and enums kept under an Enums/ folder. They report nothing unless you opt in:
<PropertyGroup>
<EnableKanjectDevAnalyzers>true</EnableKanjectDevAnalyzers>
<KanjectDeterministicCode>true</KanjectDeterministicCode>
</PropertyGroup>
Generated code is excluded from these rules. KANDEV005 compares a type's namespace with its folder using the project's RootNamespace and ProjectDir, which the package makes visible to the compiler automatically. KANDEV006 flags ambient time (DateTime.Now/UtcNow/Today, DateTimeOffset.Now/UtcNow), current culture, Environment.TickCount and unseeded Random. Guid.NewGuid() and new Random(seed) are allowed. To change a rule's severity, use .editorconfig:
[*.cs]
dotnet_diagnostic.KANDEV001.severity = warning
Diagnostics
| ID | Severity | What it means |
|---|---|---|
KANPAR001 |
Error | [Parallel] method has no partition parameter (a single T or a chunk such as ReadOnlyMemory<T>) |
KANPAR002 |
Error | MaxDegreeOfParallelism is negative |
KANPAR003 |
Error | ChunkSize is negative |
KANPAR004 |
Warning | ChunkSize is set but ChunkingMode is EvenSplit, so it is ignored |
KANPAR005 |
Warning | RetryBackoffMs is set but RetryCount is 0 |
KANPAR006 |
Warning | [Parallel] on a generic method is not generated |
KANPAR007 |
Warning | [Parallel] on a method of a nested type is not generated |
KANPAR008 |
Error | [Parallel] method has a ref/out/in parameter |
KANPAR009 |
Error | [ParallelHosted] without [Parallel] on the same method |
KANPAR010 |
Warning | [ParallelHosted(Lifetime = Scoped)] on a static method has no effect |
KANPAR011 |
Error | Type containing an instance [Parallel] method is not partial |
KANPAR012 |
Error | [Parallel] return type is not void, T, Task, Task<T>, ValueTask or ValueTask<T> |
KANPAR013 |
Info | Chunk parameter type makes each partition allocate an array; prefer ReadOnlyMemory<T> |
KANPAR014 |
Warning | [ParallelHosted] generates no hosting code in this release, so it has no effect |
KANPAR050 |
Warning | async void lambda passed to Parallel.For / ForEach / Invoke; the loop won't await it |
KANREC001 |
Error | [Recurring] return type is not supported |
KANREC002 |
Error | [Recurring] rate value is not greater than zero |
KANREC003 |
Warning | [Recurring] on a method of a nested type is not generated |
KANREC004 |
Warning | [Recurring] on a generic method is not generated |
KANREC005 |
Warning | MaxJitterMs is negative (treated as zero) |
KANREC006 |
Warning | MaxDurationSeconds is negative (treated as no limit) |
KANREC007 |
Warning | MaxIterations is negative (treated as no limit) |
KANREC008 |
Error | [Recurring] method has a ref/out/in parameter |
KANREC009 |
Error | [RecurringHosted] without [Recurring] on the same method |
KANREC010 |
Warning | [RecurringHosted(Lifetime = Scoped)] on a static method has no effect |
KANREC011 |
Error | [RecurringHosted] method takes parameters other than a single CancellationToken |
KANREC012 |
Warning | [Recurring] on an async void method; completion and exceptions can't be observed |
KANREC013 |
Error | Type containing an instance [Recurring] method is not partial |
KANQE001 |
Error | A [QueryableEnum] enum is private or protected (or nested in such a type), so the generated types can't reference it |
KAN001 |
Warning | A ConstantParameters entry has more than one value on an enum member, so no constant is generated |
KAN002 |
Error | The same [EnumParameter] name appears more than once on one enum member |
KANJECT0021 |
Error | A constant parameter value can't be turned into a valid C# identifier |
KANJECT0022 |
Error | A constant parameter value is empty or whitespace |
KANJECT0023 |
Error | Two constant parameter values produce the same identifier |
KANJECT0024 |
Error | A ConstantParameters name doesn't exist on any enum member |
KANPIC001 |
Info | Shows the severity the PrintInConsole interceptor inferred for a call site |
KANDEV001 |
Error | File declares more than one top-level type (opt-in) |
KANDEV002 |
Error | Public/internal/protected nested type declaration (opt-in) |
KANDEV003 |
Error | Enum outside an Enums/ folder (opt-in) |
KANDEV004 |
Error | File not named after the type it declares (opt-in) |
KANDEV005 |
Warning | Namespace doesn't match the folder path (opt-in) |
KANDEV006 |
Warning | Ambient nondeterministic API in code marked deterministic (opt-in) |
KANJECTGEN001 |
Error | A generator hit an unhandled exception; the message carries the exception text |
A code fix accompanies KAN001, KAN002 and KANJECT0021-KANJECT0024. It can keep only the first value, remove a duplicate [EnumParameter], or remove the offending name from ConstantParameters.
Release notes
3.12.7
PluralizerHelper.Singularize — the helper the generators use to derive an entity name from a
table or key alias — changed in three ways that were not announced at the time:
- The unchanged-plural set is now consulted, so a word whose singular and plural coincide comes
back as-is:
Status→Status(wasStatu),Series→Series. The set includesSettings, so a name derived from aSettingsalias is nowSettings, notSetting. - A trailing
ssis never treated as a plurals:Address→Address(wasAddres). - The
ies → yrule keeps the input's casing:COUNTRIES→COUNTRY(wasCOUNTRy).
Related packages
| Package | Role | Availability |
|---|---|---|
Kanject.Core |
Runtime library the generated code calls into; reference it alongside this package | nuget.org |
Kanject.Core.Annotations.Attributes |
The attribute types these generators read (a dependency of this package) | 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.
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net5.0 was computed. net5.0-windows was computed. net6.0 was computed. net6.0-android was computed. net6.0-ios was computed. net6.0-maccatalyst was computed. net6.0-macos was computed. net6.0-tvos was computed. net6.0-windows was computed. net7.0 was computed. net7.0-android was computed. net7.0-ios was computed. net7.0-maccatalyst was computed. net7.0-macos was computed. net7.0-tvos was computed. net7.0-windows was computed. net8.0 was computed. net8.0-android was computed. net8.0-browser was computed. net8.0-ios was computed. net8.0-maccatalyst was computed. net8.0-macos was computed. net8.0-tvos was computed. net8.0-windows was computed. net9.0 was computed. net9.0-android was computed. net9.0-browser was computed. net9.0-ios was computed. net9.0-maccatalyst was computed. net9.0-macos was computed. net9.0-tvos was computed. net9.0-windows was computed. net10.0 was computed. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
| .NET Core | netcoreapp2.0 was computed. netcoreapp2.1 was computed. netcoreapp2.2 was computed. netcoreapp3.0 was computed. netcoreapp3.1 was computed. |
| .NET Standard | netstandard2.0 is compatible. netstandard2.1 was computed. |
| .NET Framework | net461 was computed. net462 was computed. net463 was computed. net47 was computed. net471 was computed. net472 was computed. net48 was computed. net481 was computed. |
| MonoAndroid | monoandroid was computed. |
| MonoMac | monomac was computed. |
| MonoTouch | monotouch was computed. |
| Tizen | tizen40 was computed. tizen60 was computed. |
| Xamarin.iOS | xamarinios was computed. |
| Xamarin.Mac | xamarinmac was computed. |
| Xamarin.TVOS | xamarintvos was computed. |
| Xamarin.WatchOS | xamarinwatchos was computed. |
-
.NETStandard 2.0
- Kanject.Core.Annotations.Attributes (>= 3.14.0)
NuGet packages (6)
Showing the top 5 NuGet packages that depend on Kanject.Core.Annotations:
| Package | Downloads |
|---|---|
|
Kanject.Core
Kanject SDK core library |
|
|
Kanject.Core.Adapter.Annotations
Kanject Adapter Annotations — Source generator for service adapter endpoint, auth, and OAuth lifecycle codegen |
|
|
Kanject.Core.CloudFunction.Aws.Annotations
Source generator + analyzers that eliminate Lambda host boilerplate (the `Functions.Configuration.cs` file) for projects built on Kanject.Core.CloudFunction.Provider.AwsLambda. Pair with the `[CloudFunctionHost]` attribute shipped in Kanject.Core.CloudFunction.Abstractions. |
|
|
Kanject.Core.Queue.Provider.AwsSqs.Annotations
Kanject Core Queue AWS Sqs Annotations |
|
|
Kanject.Core.Api.Annotations
Kanject Core Api Annotations |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 3.14.0 | 42 | 10/2/2026 |
| 3.13.1 | 542 | 9/27/2026 |
| 3.13.0 | 479 | 9/27/2026 |
| 3.12.7 | 489 | 9/26/2026 |
| 3.12.6 | 559 | 9/7/2026 |
| 3.12.5 | 488 | 8/27/2026 |
| 3.12.4 | 619 | 8/22/2026 |
| 3.12.3 | 541 | 8/10/2026 |
| 3.12.2 | 524 | 8/9/2026 |
| 3.12.1 | 561 | 8/5/2026 |
| 3.12.0 | 573 | 8/5/2026 |
| 3.11.0 | 624 | 8/3/2026 |
| 3.10.6 | 611 | 7/30/2026 |
| 3.10.5 | 655 | 7/18/2026 |
| 3.10.4 | 149 | 7/13/2026 |
| 3.10.3 | 391 | 7/11/2026 |
| 3.10.2 | 465 | 7/11/2026 |
| 3.10.0 | 2,317 | 7/9/2026 |