Skip to content

Commit e6894de

Browse files
committed
feat(docs): improve lifecycle management and DI guides
- Updated lifecycle management guide to include `ILambdaLifecycleContext` usage and DI patterns. - Added examples for direct DI, AWS metadata access, state sharing, and keyed services in OnInit handlers. - Enhanced dependency injection guide with source-generated DI support for OnInit and OnShutdown. - Expanded core concepts with lifecycle context access patterns and practical usage scenarios.
1 parent 5ae7965 commit e6894de

3 files changed

Lines changed: 154 additions & 13 deletions

File tree

docs/getting-started/core-concepts.md

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,17 @@ lambda.OnShutdown(async (
8585
});
8686
```
8787

88+
### Lifecycle Context Access
89+
90+
Both OnInit and OnShutdown handlers support rich dependency injection patterns:
91+
92+
- **Inject services directly**: `(ICache cache, ILogger logger, CancellationToken ct)` – Recommended for most scenarios
93+
- **Access AWS metadata**: `(ILambdaLifecycleContext context)` – When you need environment information like function name, region, memory size, or initialization type
94+
- **Combine both**: `(ILambdaLifecycleContext context, ICache cache)` – Mix context access with direct service injection
95+
- **Use keyed services**: `([FromKeyedServices("key")] IService service)` – Resolve services registered with specific keys
96+
97+
The `ILambdaLifecycleContext` interface provides AWS environment metadata, a `Properties` dictionary for sharing state between handlers, and access to the scoped `ServiceProvider`. See [Lifecycle Management](../guides/lifecycle-management.md) for detailed patterns and examples.
98+
8899
### Lifecycle Timeline
89100

90101
```mermaid

docs/guides/dependency-injection.md

Lines changed: 47 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,23 +74,63 @@ If your handler doesn't need the Lambda payload, omit the `[FromEvent]` paramete
7474
remaining time when creating the token.
7575
- Always pass it down to outbound SDK calls and database queries so you can stop work cleanly.
7676

77-
## Middleware and Lifecycle Hooks
77+
## Middleware and Lifecycle Hooks: Source-Generated DI
7878

7979
- Middleware receives the invocation scope via the `ILambdaHostContext` argument. Resolve services with
8080
`context.ServiceProvider` or create reusable middleware classes with constructor injection.
81-
- `OnInit` and `OnShutdown` handlers run outside the normal invocation scope but each one executes
82-
inside its own scoped service provider so you can warm caches, seed connections, or flush telemetry
83-
without leaking per-invocation services.
81+
- `OnInit` and `OnShutdown` handlers now use the same source-generated dependency injection as your main
82+
handlers. Each executes inside its own scoped service provider so you can warm caches, seed connections,
83+
or flush telemetry without leaking per-invocation services.
8484

85-
```csharp title="OnInit"
86-
lambda.OnInit(async (services, ct) =>
85+
OnInit and OnShutdown handlers support multiple dependency injection patterns:
86+
87+
```csharp title="Pattern 1: Direct DI (Recommended)"
88+
lambda.OnInit(async (ICache cache, ILogger<Program> logger, CancellationToken ct) =>
8789
{
88-
var cache = services.GetRequiredService<ICache>();
90+
logger.LogInformation("Warming cache during cold start");
8991
await cache.WarmUpAsync(ct);
9092
return true;
9193
});
9294
```
9395

96+
Each handler runs in its own scoped service provider, so you can safely resolve scoped services even
97+
outside the invocation pipeline.
98+
99+
```csharp title="Pattern 2: Using ILambdaLifecycleContext"
100+
lambda.OnInit(async (ILambdaLifecycleContext context, ICache cache) =>
101+
{
102+
var logger = context.ServiceProvider.GetRequiredService<ILogger<Program>>();
103+
logger.LogInformation(
104+
"Init type: {Type}, Function: {Name}, Memory: {MB}MB",
105+
context.InitializationType,
106+
context.FunctionName,
107+
context.FunctionMemorySize
108+
);
109+
110+
await cache.WarmUpAsync(context.CancellationToken);
111+
return true;
112+
});
113+
```
114+
115+
Use `ILambdaLifecycleContext` when you need AWS environment metadata (region, function name, memory size,
116+
initialization type) or want to share data between handlers via the `Properties` dictionary.
117+
118+
```csharp title="Pattern 3: Keyed Services"
119+
builder.Services.AddKeyedSingleton<ICache, RedisCache>("redis");
120+
builder.Services.AddKeyedSingleton<ICache, MemoryCache>("memory");
121+
122+
lambda.OnInit(async (
123+
[FromKeyedServices("redis")] ICache primaryCache,
124+
[FromKeyedServices("memory")] ICache fallbackCache,
125+
CancellationToken ct
126+
) =>
127+
{
128+
await primaryCache.WarmUpAsync(ct);
129+
await fallbackCache.WarmUpAsync(ct);
130+
return true;
131+
});
132+
```
133+
94134
## Configuration and Options
95135

96136
Use the standard options pattern for configuration:

docs/guides/lifecycle-management.md

Lines changed: 96 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -132,22 +132,112 @@ Both handlers are awaited simultaneously. Keep shutdown work small—only the re
132132

133133
OnInit and OnShutdown handlers support the same source-generated dependency injection experience as middleware and handlers:
134134

135-
- Request only what you need. The generated delegate resolves typed parameters (services, keyed services, `ILambdaHostContext`, `CancellationToken`, etc.).
135+
- Request only what you need. The generated delegate resolves typed parameters (services, keyed services, `ILambdaLifecycleContext`, `CancellationToken`, etc.).
136136
- `MinimalLambda` creates a new `IServiceScope` for every handler invocation, ensuring scoped services (database units of work, caches) are isolated even though you are outside the invocation pipeline.
137-
- The `IServiceProvider` parameter gives you direct access to the scope for manual resolution when needed.
137+
- Inject `ILambdaLifecycleContext` when you need AWS environment metadata or want to share state via the `Properties` dictionary. For simple scenarios, inject services directly.
138+
139+
```csharp title="Program.cs - Keyed Services"
140+
builder.Services.AddKeyedSingleton<IMyClient, PrimaryClient>("primary");
141+
builder.Services.AddKeyedSingleton<IMyClient, SecondaryClient>("secondary");
138142

139-
```csharp title="Program.cs"
140143
lambda.OnInit(async (
141-
IServiceProvider scope,
142-
KeyedService<MyClient>("primary"),
144+
[FromKeyedServices("primary")] IMyClient primaryClient,
145+
[FromKeyedServices("secondary")] IMyClient secondaryClient,
143146
CancellationToken ct
144147
) =>
145148
{
146-
await MyWarmupAsync(scope, MyClient, ct);
149+
await primaryClient.WarmupAsync(ct);
150+
await secondaryClient.WarmupAsync(ct);
151+
return true;
152+
});
153+
```
154+
155+
## Accessing AWS Environment Information
156+
157+
When you need AWS environment metadata during initialization or shutdown, inject `ILambdaLifecycleContext`. This interface provides rich context information about the Lambda execution environment beyond what's available in the standard invocation pipeline.
158+
159+
### Using ILambdaLifecycleContext
160+
161+
```csharp title="Program.cs - AWS Environment Metadata"
162+
lambda.OnInit(async (ILambdaLifecycleContext context, ILogger<Program> logger) =>
163+
{
164+
logger.LogInformation(
165+
"Initializing {FunctionName} v{Version} in {Region}",
166+
context.FunctionName,
167+
context.FunctionVersion,
168+
context.Region
169+
);
170+
171+
logger.LogInformation(
172+
"Memory: {Memory}MB, Init type: {InitType}, Elapsed: {Elapsed}ms",
173+
context.FunctionMemorySize,
174+
context.InitializationType,
175+
context.ElapsedTime.TotalMilliseconds
176+
);
177+
147178
return true;
148179
});
149180
```
150181

182+
### Sharing State via Properties Dictionary
183+
184+
The `Properties` dictionary lets you share data between OnInit handlers or pass information from initialization to your invocation handlers.
185+
186+
```csharp title="Program.cs - State Sharing"
187+
lambda.OnInit(async (ILambdaLifecycleContext context) =>
188+
{
189+
var initStartTime = DateTimeOffset.UtcNow;
190+
context.Properties["InitStartTime"] = initStartTime;
191+
context.Properties["EnvironmentType"] = context.InitializationType;
192+
193+
return true;
194+
});
195+
196+
lambda.OnInit(async (ILambdaLifecycleContext context, ILogger<Program> logger) =>
197+
{
198+
if (context.Properties.TryGetValue("InitStartTime", out var startTime))
199+
{
200+
logger.LogInformation("First handler started at {Time}", startTime);
201+
}
202+
203+
return true;
204+
});
205+
```
206+
207+
The `Properties` dictionary is backed by a thread-safe `ConcurrentDictionary<string, object?>` and is shared across all OnInit handlers. Properties set during OnInit are also available to invocation handlers via `ILambdaHostContext.Properties`.
208+
209+
### Available Context Properties
210+
211+
`ILambdaLifecycleContext` exposes the following properties:
212+
213+
| Property | Type | Description |
214+
|----------|------|-------------|
215+
| `CancellationToken` | `CancellationToken` | Signals cancellation when `InitTimeout` (OnInit) or `ShutdownDuration - ShutdownDurationBuffer` (OnShutdown) elapses, or when SIGTERM is received |
216+
| `ServiceProvider` | `IServiceProvider` | The scoped service container for this handler invocation. Use for manual service resolution when direct injection isn't sufficient |
217+
| `Properties` | `IDictionary<string, object?>` | Thread-safe dictionary for sharing data between handlers or from OnInit to invocation handlers |
218+
| `ElapsedTime` | `TimeSpan` | Time elapsed since the Lambda execution environment started |
219+
| `Region` | `string?` | AWS region where the function is running (from `AWS_REGION` env var) |
220+
| `ExecutionEnvironment` | `string?` | Runtime identifier like `AWS_Lambda_dotnet8` (from `AWS_EXECUTION_ENV` env var) |
221+
| `FunctionName` | `string?` | Name of the Lambda function (from `AWS_LAMBDA_FUNCTION_NAME` env var) |
222+
| `FunctionMemorySize` | `int?` | Memory allocated to the function in MB (from `AWS_LAMBDA_FUNCTION_MEMORY_SIZE` env var) |
223+
| `FunctionVersion` | `string?` | Version of the function being executed (from `AWS_LAMBDA_FUNCTION_VERSION` env var) |
224+
| `InitializationType` | `string?` | Type of initialization: `on-demand`, `provisioned-concurrency`, `snap-start`, or `lambda-managed-instances` (from `AWS_LAMBDA_INITIALIZATION_TYPE` env var) |
225+
| `LogGroupName` | `string?` | CloudWatch Logs group name (from `AWS_LAMBDA_LOG_GROUP_NAME` env var, not available in SnapStart) |
226+
| `LogStreamName` | `string?` | CloudWatch Logs stream name (from `AWS_LAMBDA_LOG_STREAM_NAME` env var, not available in SnapStart) |
227+
| `TaskRoot` | `string?` | Path to the Lambda function code (from `LAMBDA_TASK_ROOT` env var) |
228+
229+
All AWS environment properties are nullable because they depend on environment variables set by the Lambda runtime. Most are available during normal execution, but some (like `LogGroupName` and `LogStreamName`) are unavailable in SnapStart functions.
230+
231+
### When to Use ILambdaLifecycleContext
232+
233+
Use `ILambdaLifecycleContext` when you need:
234+
235+
- **AWS environment metadata** – Access region, function name, memory size, or initialization type for logging, metrics, or conditional logic based on the execution environment
236+
- **State sharing** – Pass data between OnInit handlers or from OnInit to invocation handlers via the `Properties` dictionary
237+
- **Manual service resolution** – Access `ServiceProvider` directly for advanced scenarios where parameter injection isn't sufficient
238+
239+
For simple dependency injection scenarios, prefer injecting services directly as parameters instead of using `ILambdaLifecycleContext.ServiceProvider`.
240+
151241
## Configuring Lifecycle Behavior
152242

153243
Use `ConfigureLambdaHostOptions` to shape lifecycle behavior centrally or bind the same settings from configuration:

0 commit comments

Comments
 (0)