Getting Started¶
This guide gets ContextR running in minutes for a typical ASP.NET Core service with outgoing HttpClient calls.
1) Pick packages¶
Minimum for ambient context only:
For propagation with mapping:
dotnet add package ContextR
dotnet add package ContextR.Propagation
dotnet add package ContextR.Propagation.Mapping
For web + outgoing HTTP propagation:
Optional strategy packages:
ContextR.Propagation.InlineJsonfor non-primitive payload serializationContextR.Propagation.Chunkingfor oversize chunk split/reassemblyContextR.Propagation.Tokenfor token/reference contracts
2) Define a context contract¶
public sealed class UserContext
{
public string? TraceId { get; set; }
public string? TenantId { get; set; }
public string? UserId { get; set; }
}
3) Register ContextR¶
builder.Services.AddContextR(ctx =>
{
ctx.Add<UserContext>(reg => reg
.MapProperty(c => c.TraceId, "X-Trace-Id")
.MapProperty(c => c.TenantId, "X-Tenant-Id")
.MapProperty(c => c.UserId, "X-User-Id")
.UseAspNetCore()
.UseGlobalHttpPropagation());
});
4) Build and run app¶
No explicit app.UseContextR() call is required.
UseAspNetCore() registers extraction middleware automatically through IStartupFilter.
5) Use context in app code¶
Prefer snapshots in application services:
public sealed class OrderService
{
private readonly IContextSnapshot _snapshot;
public OrderService(IContextSnapshot snapshot)
{
_snapshot = snapshot;
}
public string? GetTenant() => _snapshot.GetContext<UserContext>()?.TenantId;
}
6) Add complex property support (optional)¶
ctx.Add<ExtendedContext>(reg => reg
.UseInlineJsonPayloads<ExtendedContext>(o =>
{
o.MaxPayloadBytes = 2048;
o.OversizeBehavior = ContextOversizeBehavior.SkipProperty;
})
.MapProperty(c => c.Tags, "X-Tags"));
7) Validate behavior with tests¶
Recommended first tests:
- incoming header extraction fills context
- outgoing
HttpClientincludes mapped headers - required property failure behavior matches policy
- oversize behavior is deterministic
For full examples, see samples.
Optional: fail requests when required ingress context is missing¶
builder.Services.AddContextR(ctx =>
{
ctx.Add<UserContext>(reg => reg
.Map(m => m
.ByConvention()
.Property(c => c.TenantId, "X-Tenant-Id")
.Property(c => c.TraceId, "X-Trace-Id")
.Property(c => c.UserId, "X-User-Id"))
.UseAspNetCore(o => o.Enforcement(e =>
{
e.Mode = ContextIngressEnforcementMode.FailRequest;
e.OnFailure = _ => ContextIngressFailureDecision.Fail(400, "Required context is missing.");
}))
.UseGlobalHttpPropagation());
});
Need service access while configuring ASP.NET Core transport options?
builder.Services.AddContextR(ctx =>
{
ctx.Add<UserContext>(reg => reg
.Map(m => m.ByConvention().Property(c => c.TenantId, "X-Tenant-Id"))
.UseAspNetCore((sp, o) =>
{
var logger = sp.GetRequiredService<ILogger<Startup>>();
o.Enforcement(e =>
{
e.Mode = ContextIngressEnforcementMode.ObserveOnly;
e.OnFailure = _ =>
{
logger.LogWarning("Context ingress validation issue.");
return ContextIngressFailureDecision.Continue();
};
});
}));
});
Optional: Ingress resolution (gateway / first-hop)¶
If you also want to resolve context at ingress (for example from JWT claims), add ContextR.Resolution and configure UseResolver(...).
builder.Services.AddContextR(ctx =>
{
ctx.Add<UserContext>(reg => reg
.AddResolution(r => r
.UseResolver(_ => new UserContext { UserId = "resolved-user" })));
});
Notes:
UseResolver(...)andUseResolutionPolicy(...)auto-register resolution services.AddContextRResolution()is only for advanced scenarios where you need orchestrator/policy services before resolver/policy registration.