Kanject.Core.CloudFunction.Aws.Annotations
1.8.2
Prefix Reserved
dotnet add package Kanject.Core.CloudFunction.Aws.Annotations --version 1.8.2
NuGet\Install-Package Kanject.Core.CloudFunction.Aws.Annotations -Version 1.8.2
<PackageReference Include="Kanject.Core.CloudFunction.Aws.Annotations" Version="1.8.2" />
<PackageVersion Include="Kanject.Core.CloudFunction.Aws.Annotations" Version="1.8.2" />
<PackageReference Include="Kanject.Core.CloudFunction.Aws.Annotations" />
paket add Kanject.Core.CloudFunction.Aws.Annotations --version 1.8.2
#r "nuget: Kanject.Core.CloudFunction.Aws.Annotations, 1.8.2"
#:package Kanject.Core.CloudFunction.Aws.Annotations@1.8.2
#addin nuget:?package=Kanject.Core.CloudFunction.Aws.Annotations&version=1.8.2
#tool nuget:?package=Kanject.Core.CloudFunction.Aws.Annotations&version=1.8.2
Kanject.Core.CloudFunction.Aws.Annotations
A Roslyn source generator, with analyzers and code fixes, that removes the hand-written plumbing from AWS Lambda hosts built on Kanject.Core.CloudFunction.Provider.AwsLambda. You mark a partial class with [CloudFunctionHost] and write ConfigureServices. The generator emits everything else: the base class, the constructor, OnStartup, the assembly-level Lambda serializer, and bind-or-throw checks for required configuration sections.
Installation
<ItemGroup>
<PackageReference Include="Kanject.Core.CloudFunction.Provider.AwsLambda" />
<PackageReference Include="Kanject.Core.CloudFunction.Aws.Annotations" PrivateAssets="all" />
<PackageReference Include="Amazon.Lambda.Serialization.SystemTextJson" />
</ItemGroup>
If you don't use central package management, add Version="x.y.z" to each reference. From the command line:
dotnet add package Kanject.Core.CloudFunction.Aws.Annotations
Things to know about how this package is wired:
- Reference it directly.
Kanject.Core.CloudFunction.Provider.AwsLambdadepends on this package. The provider's nuspec lists that dependency withexclude="Build,Analyzers", yet a project that references only the provider still gets the generator and its build targets (seen with .NET SDK 9.0.311 and 10.0.402). Don't rely on either behaviour. Reference the package yourself, so the generator version is explicit and doesn't depend on how NuGet resolves the provider's dependency. - Add
PrivateAssets="all"yourself. The package is not marked as a development dependency, sodotnet add packagewrites a plain reference.PrivateAssets="all"stops the generator from flowing to projects that reference yours. - There is no separate
.Attributespackage.[CloudFunctionHost]and[RequireConfigSection<T>]live inKanject.Core.CloudFunction.Abstractions, which comes with the provider. - The generated code needs the provider. It derives from the provider's
CloudFunction. By default it also referencesAmazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer. WithConfigurationSource.AwsSystemManagerParameterStore, also referenceKanject.Core.Api.Aws.Extensions. - Frameworks and compiler. The package targets
netstandard2.0because it runs inside the compiler. Use it from projects that target .NET 8, .NET 9 or .NET 10 (the provider's frameworks). It is built against Roslyn 4.14 (Microsoft.CodeAnalysis.CSharp4.14.0), so build with .NET SDK 9.0.300 or later, or Visual Studio 2022 17.14 or later. An older compiler would skip the generator (CS9057); the package's build targets stop the build with KANTC001 instead. - MSBuild defaults. The package's build targets set
EnableConfigurationBindingGeneratortotruefor .NET 8+ projects unless you've already set it, so the generatedGet<T>()calls bind without reflection. Set it tofalsein your project to opt out. Whenever the binder generator is on, whether by this default or by your own setting, the targets also addMicrosoft.Extensions.Configuration.Binder.SourceGenerationtoInterceptorsNamespaces. Without that, a .NET 9 SDK build of a project with aMicrosoft.AspNetCore.Appframework reference fails with CS9137.
Quick start
Before: a hand-written host.
using Amazon.Lambda.Core;
using Kanject.Core.Api.Aws.Extensions.Vault.ParameterStore;
using Kanject.Core.CloudFunction.Abstractions.Enums;
using Kanject.Core.CloudFunction.Provider.AwsLambda;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
[assembly: LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace Orders.Lambda;
public partial class Functions : CloudFunction
{
#if DEBUG
public Functions() : base(CloudFunctionEnvironment.Development) { }
#else
public Functions() : base() { }
#endif
public override void OnStartup(IConfigurationBuilder configurationBuilder)
{
#if DEBUG
configurationBuilder.AddAwsSystemManagerParameterStore(fetchSecretPathFromAppSettings: true);
#else
configurationBuilder.AddAwsSystemManagerParameterStore();
#endif
}
public override void ConfigureServices(IServiceCollection services)
{
var appSettings = Configuration.GetSection("AppSettings").Get<AppSettings>()
?? throw new InvalidOperationException("'AppSettings' is required.");
services.AddSingleton(appSettings);
services.AddScoped<OrderService>();
}
public override void Configure(IServiceProvider serviceProvider) { }
}
After: the same host with the generator. AppSettings and OrderService are your own types.
using Kanject.Core.CloudFunction.Abstractions.Attributes;
using Kanject.Core.CloudFunction.Abstractions.Enums;
using Microsoft.Extensions.DependencyInjection;
namespace Orders.Lambda;
[CloudFunctionHost(
DebugEnvironment = CloudFunctionEnvironment.Development,
ConfigurationSource = ConfigurationSource.AwsSystemManagerParameterStore)]
[RequireConfigSection<AppSettings>("AppSettings")]
public partial class Functions
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddSingleton(AppSettings); // generated property, already bound and validated
services.AddScoped<OrderService>();
}
}
Your Lambda handler methods stay where they were, in other partial files of the same class. The Kanject.Core.CloudFunction.Provider.AwsLambda package covers handlers, scopes and runtime behavior.
What gets generated
For each [CloudFunctionHost] class, the generator adds one file, {ClassName}.CloudFunctionHost.g.cs. The following is what it emits for the "after" example in a Debug build (license header, #nullable enable and doc comments omitted):
// <auto-generated/>
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Kanject.Core.CloudFunction.Abstractions.Enums;
using Kanject.Core.CloudFunction.Provider.AwsLambda;
using Kanject.Core.Api.Aws.Extensions.Vault.ParameterStore;
[assembly: global::Amazon.Lambda.Core.LambdaSerializerAttribute(typeof(global::Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace Orders.Lambda;
partial class Functions : global::Kanject.Core.CloudFunction.Provider.AwsLambda.CloudFunction
{
public Functions() : base(global::Kanject.Core.CloudFunction.Abstractions.Enums.CloudFunctionEnvironment.Development) { }
public global::Orders.Lambda.AppSettings AppSettings { get; private set; } = null!;
public override void OnStartup(global::Microsoft.Extensions.Configuration.IConfigurationBuilder configurationBuilder)
{
configurationBuilder.AddAwsSystemManagerParameterStore(fetchSecretPathFromAppSettings: true);
AddAdditionalConfigurationSources(configurationBuilder);
}
partial void AddAdditionalConfigurationSources(global::Microsoft.Extensions.Configuration.IConfigurationBuilder configurationBuilder);
protected override void ConfigureLogging(global::Microsoft.Extensions.Logging.ILoggingBuilder logging)
{
AddAdditionalLoggingProviders(logging);
}
partial void AddAdditionalLoggingProviders(global::Microsoft.Extensions.Logging.ILoggingBuilder logging);
protected override void RegisterFrameworkServices(global::Microsoft.Extensions.DependencyInjection.IServiceCollection services)
{
var validationConfiguration = base.Configuration;
this.AppSettings = validationConfiguration.GetSection("AppSettings").Get<global::Orders.Lambda.AppSettings>()
?? throw new global::System.InvalidOperationException("Required configuration section 'AppSettings' is missing or could not bind to global::Orders.Lambda.AppSettings.");
}
}
The generator reads the compilation's DEBUG symbol and writes only the matching branch, so the output contains no #if directives. A Release build of the same class emits public Functions() : base() { } and configurationBuilder.AddAwsSystemManagerParameterStore();. Deploy Release builds: a Debug build bakes DebugEnvironment into the constructor.
In Release, the Kanject.Core.Api.Aws.Extensions Parameter Store source reads the parameter path from the SECRET_PARAMETER_PATHNAME environment variable. In Debug with DebugFetchSecretPathFromAppSettings (the default), it reads AppSettings:SecretParameterPathName, AppSettings:AwsRegion and AppSettings:AwsProfile from the configuration built so far.
[CloudFunctionHost] options
| Property | Default | Effect on the generated code |
|---|---|---|
DebugEnvironment |
not set | In Debug builds, the constructor passes this CloudFunctionEnvironment to the base. If you leave it unset, both configurations use base(), and the environment comes from ASPNETCORE_LAMBDA_ENVIRONMENT / ASPNETCORE_ENVIRONMENT. |
ConfigurationSource |
None |
AwsSystemManagerParameterStore adds configurationBuilder.AddAwsSystemManagerParameterStore(...) to OnStartup. |
DebugFetchSecretPathFromAppSettings |
true |
With Parameter Store, Debug builds call the fetchSecretPathFromAppSettings: true overload. Set it to false to emit the parameterless call in both configurations. |
EmitLambdaSerializer |
true |
Emits [assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))]. Set it to false if the project declares its own serializer, which a project with Amazon.Lambda.Annotations [LambdaFunction] handlers must do (see below). |
EnableAot |
false |
Derives from Kanject.Core.CloudFunction.Provider.AwsLambda.Aot.CloudFunction instead of the default host. |
Provider |
AwsLambda |
The only supported value. |
Required configuration sections
Each [RequireConfigSection<T>("Section")] adds two things:
- A bind-or-throw check in a generated
RegisterFrameworkServicesoverride. The base host calls it after building its configuration from every source (the attribute-driven ones and yourAddAdditionalConfigurationSourceshook) and beforeConfigureServices. The check reads that configuration, so no source, Parameter Store included, loads twice. If the section is missing, or doesn't bind toT, the cold start fails withInvalidOperationExceptionand names the section. - A public property holding the bound instance. The property exists by the time
ConfigureServicesruns. The generator names it fromPropertyNameif you set it, otherwise from the section name if that is a valid C# identifier, otherwise fromT's simple name. If an explicitPropertyNameis not a valid identifier, the check still runs but no property is emitted.
[CloudFunctionHost]
[RequireConfigSection<AppSettings>("AppSettings")] // property: AppSettings
[RequireConfigSection<QueueSettings>("Queues:Orders", PropertyName = "OrderQueue")] // property: OrderQueue
public partial class Functions { /* ... */ }
T can be a class or a struct. For a class (or a nullable struct such as int?) the property is initialised with null!, and validation throws when the section is missing or binds to null. A non-nullable struct can't be null, so its property starts at default and validation throws when the section doesn't exist. It does not register IOptions<T>; call services.Configure<T>(...) in ConfigureServices if you want options binding too. If two attributes resolve to the same property name, you get a duplicate-member compile error. Set PropertyName on one of them to fix it.
Extension points
The generator owns OnStartup and ConfigureLogging, and RegisterFrameworkServices when the class has a [RequireConfigSection<T>] or sets EnablePrintInConsoleLogging. It declares two optional partial-method hooks for you to implement. If you leave a hook unimplemented, the compiler removes its call.
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
namespace Orders.Lambda;
public partial class Functions
{
// Runs at the end of the generated OnStartup, after the attribute-driven sources. The sources it
// adds count for [RequireConfigSection] validation, which runs once the host has built them all.
partial void AddAdditionalConfigurationSources(IConfigurationBuilder configurationBuilder)
{
configurationBuilder.AddJsonFile("features.json", optional: true);
}
// Runs after the base host's ClearProviders() + AddConsole().
partial void AddAdditionalLoggingProviders(ILoggingBuilder logging)
{
logging.SetMinimumLevel(LogLevel.Information);
}
}
Configure(IServiceProvider) remains yours to override for post-container work. If you need full control of OnStartup, remove the attribute and write the host by hand (KANCF003 enforces this).
Handlers with Amazon.Lambda.Annotations
If your handlers use Amazon.Lambda.Annotations' [LambdaFunction], set EmitLambdaSerializer = false and declare the serializer in your own source:
using Amazon.Lambda.Annotations;
using Kanject.Core.CloudFunction.Abstractions.Attributes;
using Kanject.Core.CloudFunction.Abstractions.Enums;
using Microsoft.Extensions.DependencyInjection;
[assembly: Amazon.Lambda.Core.LambdaSerializer(typeof(Amazon.Lambda.Serialization.SystemTextJson.DefaultLambdaJsonSerializer))]
namespace Orders.Lambda;
[CloudFunctionHost(DebugEnvironment = CloudFunctionEnvironment.Development, EmitLambdaSerializer = false)]
public partial class Functions
{
public override void ConfigureServices(IServiceCollection services)
{
services.AddScoped<OrderService>();
}
[LambdaFunction]
public async Task<OrderReceipt> PlaceOrder(PlaceOrderRequest request)
{
using var scope = ServiceScope;
return await scope.ServiceProvider.GetRequiredService<OrderService>().PlaceAsync(request);
}
}
Amazon's generator needs [LambdaSerializer] on each [LambdaFunction] method or on the assembly, and it reads only the source you wrote. Source generators can't see each other's output, so the attribute this generator emits never counts. Leave EmitLambdaSerializer on and the build fails with AWSLambda0108 ("Assembly attribute Amazon.Lambda.Core.LambdaSerializerAttribute is missing").
- KANCF005 reports the same failure on the
[CloudFunctionHost]attribute and says what to change. Its code fix, Declare the Lambda serializer by hand, setsEmitLambdaSerializer = falseand writes the attribute above into the host's file. It names the sameDefaultLambdaJsonSerializerthe generator emits, so serialization doesn't change. - A method with its own serializer. A
[LambdaFunction]method that carries[LambdaSerializer]itself doesn't need the assembly attribute. - Handlers on another class. KANCF005 also fires for
[LambdaFunction]methods outside the host class, because the assembly attribute covers them too. - Construction. Without
[LambdaStartup], Amazon's generated wrapper creates the class with its parameterless constructor, the one this generator emits, so the host starts as it does for a hand-written handler. - Native AOT. Declare a source-generated serializer instead (see Native AOT).
Migrating an existing host
The migration analyzer reports KANCF100 (Info) on a hand-written host that the generator can replace. The code fix, Migrate to [CloudFunctionHost], rewrites it. The analyzer only fires when all of these are true:
- the class derives directly from
Kanject.Core.CloudFunction.Provider.AwsLambda.CloudFunction, ispartial, is not abstract, and doesn't already carry the attribute; - every declared constructor is parameterless and calls
base()orbase(CloudFunctionEnvironment.X); OnStartupis empty or contains exactly oneAddAwsSystemManagerParameterStore(...)call;Configure(IServiceProvider)is absent or empty, andConfigureServicesis overridden;- the assembly declares
[assembly: LambdaSerializer(typeof(DefaultLambdaJsonSerializer))].
The fix does the following:
- Adds
[CloudFunctionHost(...)], inferringDebugEnvironmentfrom the constructor andConfigurationSourcefromOnStartup. - Removes the constructors,
OnStartup, the emptyConfigureand the: CloudFunctionbase. - Removes the
LambdaSerializerassembly attribute when it is in the same file. If a[LambdaFunction]method relies on it, the fix keeps it and addsEmitLambdaSerializer = falseinstead. - Keeps
ConfigureServicesexactly as written.
Afterwards, check these by hand:
- Run the fix in a Debug configuration so it can see a constructor behind
#if DEBUG. - If your old Debug branch called
AddAwsSystemManagerParameterStore()without arguments, addDebugFetchSecretPathFromAppSettings = false. The fix leaves that property at its default oftrue. - Remove a
LambdaSerializerattribute that lives in another file (unless[LambdaFunction]methods need it), and any usings that are now unused.
Native AOT
EnableAot = true only selects the AOT-oriented host base class. The functions you register in ConfigureServices must be AOT-safe as well. DefaultLambdaJsonSerializer uses reflection-based System.Text.Json, so for an AOT function set EmitLambdaSerializer = false and declare your own source-generated serializer, such as SourceGeneratorLambdaJsonSerializer<TContext> from Amazon.Lambda.Serialization.SystemTextJson.
Diagnostics
| ID | Severity | What it means |
|---|---|---|
| KANCF001 | Error | The [CloudFunctionHost] class is not partial (code fix: Make class partial). |
| KANCF003 | Error | The class declares its own OnStartup override, which the generator owns. Use AddAdditionalConfigurationSources, change ConfigurationSource, or drop the attribute. |
| KANCF004 | Error | More than one [CloudFunctionHost] class exists in the assembly (the Lambda serializer attribute is assembly-wide). |
| KANCF005 | Error | The host leaves EmitLambdaSerializer on, but a [LambdaFunction] method needs a serializer Amazon.Lambda.Annotations can see (it reports AWSLambda0108). Code fix: Declare the Lambda serializer by hand. |
| KANCF100 | Info | A hand-written host matches the migration shape (code fix: Migrate to [CloudFunctionHost]). |
There is no KANCF002. If the class declares a base type other than CloudFunction, the compiler rejects it, because the generated partial fixes the base class.
Related packages
| Package | Role | Availability |
|---|---|---|
Kanject.Core.CloudFunction.Provider.AwsLambda |
The Lambda host runtime whose CloudFunction base the generated code extends |
nuget.org |
Kanject.Core.CloudFunction.Abstractions |
Defines [CloudFunctionHost], [RequireConfigSection<T>] and the option enums (arrives with the provider) |
nuget.org |
Kanject.Core.Api.Aws.Extensions |
Supplies AddAwsSystemManagerParameterStore for ConfigurationSource.AwsSystemManagerParameterStore |
nuget.org |
Kanject.Core.Queue.Provider.AwsSqs |
SQS queue consumers that you can dispatch from the host's Lambda handlers | 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.15.0)
NuGet packages (1)
Showing the top 1 NuGet packages that depend on Kanject.Core.CloudFunction.Aws.Annotations:
| Package | Downloads |
|---|---|
|
Kanject.Core.CloudFunction.Provider.AwsLambda
Kanject core cloud function aws lambda provider |
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.8.2 | 6 | 11/2/2026 |
| 1.8.1 | 245 | 10/14/2026 |
| 1.8.0 | 217 | 10/5/2026 |
| 1.7.0 | 119 | 10/2/2026 |
| 1.6.1 | 141 | 9/27/2026 |
| 1.6.0 | 114 | 9/27/2026 |
| 1.5.7 | 130 | 9/26/2026 |
| 1.5.6 | 160 | 9/7/2026 |
| 1.5.5 | 139 | 8/27/2026 |
| 1.5.4 | 151 | 8/22/2026 |
| 1.5.3 | 152 | 8/10/2026 |
| 1.5.2 | 143 | 8/9/2026 |
| 1.5.1 | 139 | 8/5/2026 |
| 1.5.0 | 141 | 8/5/2026 |
| 1.4.0 | 156 | 8/3/2026 |
| 1.3.5 | 160 | 7/30/2026 |
| 1.3.4 | 162 | 7/18/2026 |
| 1.3.3 | 159 | 7/13/2026 |
| 1.3.2 | 156 | 7/11/2026 |
| 1.3.1 | 154 | 7/11/2026 |
- KANCF005 explains AWSLambda0108 beside [LambdaFunction] methods and its code fix declares the Lambda serializer by hand. The KANCF100 migration no longer deletes that serializer.
- [RequireConfigSection] binds from the configuration the host already built so Parameter Store loads once per cold start instead of twice.
- Single-targeted .NET 8+ projects get the configuration binding generator by default and .NET 9 SDK builds no longer fail with CS9137.