This guide demonstrates the core patterns for using the Context System in your .NET applications, including creating, accessing, and executing code within a context.
Define a context class implementing IContext:
using Core.Features.Context.Abstractions;
public class MyAppContext(string userId, string tenantId) : IContext
{
public string Id { get; } = Guid.NewGuid().ToString();
public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
public string UserId { get; } = userId;
public string TenantId { get; } = tenantId;
public string CorrelationId { get; } = Guid.NewGuid().ToString();
}Notes:
- ID and CreatedAt are mandatory for all contexts
- Add any additional properties required for your application
- Keep contexts immutable for thread safety
var services = new ServiceCollection();
// Registers context infrastructure
services.AddContext<MyAppContext>();
services.AddReadOnlyContextAccessor<MyAppContext>();
var sp = services.BuildServiceProvider();What gets registered:
AddContext<TContext>()registers:-
ContextStore<TContext>
-
IContextAccessor<TContext>
-
IContextManager<TContext>
AddReadOnlyContextAccessor<TContext>()registers:-
IContextAccessor<IReadOnlyContext>for untrusted code
Inject IContextAccessor<TContext> in your services:
public class OrderService(IContextAccessor<MyAppContext> contextAccessor)
{
public void ProcessOrder()
{
var ctx = contextAccessor.RequireCurrent();
Console.WriteLine($"Processing order for user: {ctx.UserId}, tenant: {ctx.TenantId}");
}
}Available methods:
| Method | Behavior |
|---|---|
RequireCurrent() |
Returns context; throws if no context is active |
TryGetCurrent() |
Returns context or null |
GetCurrent() |
Returns context or null; no exception |
HasContext() |
Returns true if context is active |
var manager = sp.GetRequiredService<IContextManager<MyAppContext>>();
var service = sp.GetRequiredService<OrderService>();
var context = new MyAppContext("user-123", "tenant-456");
await manager.ExecuteInContext(context, async () =>
{
service.ProcessOrder(); // Context flows automatically
await Task.Delay(10); // Context remains available in async calls
});Key features:
ExecuteInContextsets the context for the duration of the action- Context is automatically restored/cleaned up afterward
- Works seamlessly with async and multithreaded code
- Supports nesting and context switching
public class AuditService(IContextAccessor<IReadOnlyContext> context)
{
public void Log()
{
var ctx = context.RequireCurrent();
Console.WriteLine($"[ReadOnly] Context ID: {ctx.Id}, CreatedAt: {ctx.CreatedAt}");
}
}Security benefits:
- Read-only accessor ensures untrusted code cannot modify context
- Only
IdandCreatedAtare visible - Hides all custom application properties (UserId, TenantId, etc.)
// 1. Define context
public class UserContext(string userId) : IContext
{
public string Id { get; } = Guid.NewGuid().ToString();
public DateTimeOffset CreatedAt { get; } = DateTimeOffset.UtcNow;
public string UserId { get; } = userId;
public string RequestId { get; } = Guid.NewGuid().ToString();
}
// 2. Register in DI
var services = new ServiceCollection();
services.AddContext<UserContext>();
services.AddReadOnlyContextAccessor<UserContext>();
// 3. Create service
public class UserService(IContextAccessor<UserContext> context)
{
public string GetCurrentUserId()
{
return context.RequireCurrent().UserId;
}
}
// 4. Execute with context
var provider = services.BuildServiceProvider();
var manager = provider.GetRequiredService<IContextManager<UserContext>>();
var userService = provider.GetRequiredService<UserService>();
await manager.ExecuteInContext(new UserContext("alice"), () =>
{
var userId = userService.GetCurrentUserId(); // Returns "alice"
Console.WriteLine($"Current user: {userId}");
});- Define a context class implementing
IContext - Register context infrastructure in DI using
AddContextandAddReadOnlyContextAccessor - Access context in services with:
IContextAccessor<TContext>for trusted codeIContextAccessor<IReadOnlyContext>for untrusted code
- Use ExecuteInContext to scope context for operations and async flows
- Read-only snapshots prevent untrusted code from modifying sensitive data
- Core Concepts - Understanding the architecture
Built with ❤️ for .NET developers