Kanject.Core.CloudFunction.Aws.Annotations
1.7.0
Prefix Reserved
dotnet add package Kanject.Core.CloudFunction.Aws.Annotations --version 1.7.0
NuGet\Install-Package Kanject.Core.CloudFunction.Aws.Annotations -Version 1.7.0
<PackageReference Include="Kanject.Core.CloudFunction.Aws.Annotations" Version="1.7.0" />
<PackageVersion Include="Kanject.Core.CloudFunction.Aws.Annotations" Version="1.7.0" />
<PackageReference Include="Kanject.Core.CloudFunction.Aws.Annotations" />
paket add Kanject.Core.CloudFunction.Aws.Annotations --version 1.7.0
#r "nuget: Kanject.Core.CloudFunction.Aws.Annotations, 1.7.0"
#:package Kanject.Core.CloudFunction.Aws.Annotations@1.7.0
#addin nuget:?package=Kanject.Core.CloudFunction.Aws.Annotations&version=1.7.0
#tool nuget:?package=Kanject.Core.CloudFunction.Aws.Annotations&version=1.7.0
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, so the generator can also reach your project transitively. Reference it yourself anyway, 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 5.6 (Microsoft.CodeAnalysis.CSharp5.6.0), so build with an SDK whose compiler is at least that version. An older compiler skips the generator and reports CS9057. - MSBuild default. The package's props 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.
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);
var validationConfiguration = configurationBuilder.Build();
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.");
}
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);
}
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. |
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 at the end of
OnStartup. It runs after the attribute-driven sources and yourAddAdditionalConfigurationSourceshook. 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. The validation builds the configuration one extra time per cold start. 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 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 and before
// [RequireConfigSection] validation.
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).
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. - 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, 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). |
| 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.14.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.7.0 | 39 | 10/2/2026 |
| 1.6.1 | 121 | 9/27/2026 |
| 1.6.0 | 95 | 9/27/2026 |
| 1.5.7 | 104 | 9/26/2026 |
| 1.5.6 | 149 | 9/7/2026 |
| 1.5.5 | 124 | 8/27/2026 |
| 1.5.4 | 136 | 8/22/2026 |
| 1.5.3 | 139 | 8/10/2026 |
| 1.5.2 | 127 | 8/9/2026 |
| 1.5.1 | 122 | 8/5/2026 |
| 1.5.0 | 125 | 8/5/2026 |
| 1.4.0 | 142 | 8/3/2026 |
| 1.3.5 | 145 | 7/30/2026 |
| 1.3.4 | 148 | 7/18/2026 |
| 1.3.3 | 145 | 7/13/2026 |
| 1.3.2 | 143 | 7/11/2026 |
| 1.3.1 | 140 | 7/11/2026 |
| 1.3.0 | 159 | 7/9/2026 |
| 1.2.1 | 150 | 7/9/2026 |