VapeCache.Extensions.Aspire
1.2.12
See the version list below for details.
dotnet add package VapeCache.Extensions.Aspire --version 1.2.12
NuGet\Install-Package VapeCache.Extensions.Aspire -Version 1.2.12
<PackageReference Include="VapeCache.Extensions.Aspire" Version="1.2.12" />
<PackageVersion Include="VapeCache.Extensions.Aspire" Version="1.2.12" />
<PackageReference Include="VapeCache.Extensions.Aspire" />
paket add VapeCache.Extensions.Aspire --version 1.2.12
#r "nuget: VapeCache.Extensions.Aspire, 1.2.12"
#:package VapeCache.Extensions.Aspire@1.2.12
#addin nuget:?package=VapeCache.Extensions.Aspire&version=1.2.12
#tool nuget:?package=VapeCache.Extensions.Aspire&version=1.2.12
VapeCache.Extensions.Aspire
Wire VapeCache into .NET Aspire without Program.cs boilerplate. You get service discovery, health checks, telemetry, and wrapper endpoints in one fluent chain.
Features
✅ Service Discovery - Auto-configure Redis connection from Aspire resources
✅ Health Checks - Redis connectivity and circuit breaker monitoring
✅ Telemetry - Cache/Redis metrics visible in Aspire Dashboard (plus EF Core cache telemetry when installed)
✅ Distributed Tracing - End-to-end traces for Redis operations (plus EF Core cache traces when installed)
✅ Wrapper Endpoints - MapVapeCacheEndpoints(...) for diagnostics and MapVapeCacheAdminEndpoints(...) for control routes
✅ SEQ by Default - OTLP exporter falls back to Seq when no endpoint is configured
✅ Fluent Telemetry API - .UseSeq(...), custom headers, and wrapper callbacks
✅ Fluent Stampede Profiles - .WithCacheStampedeProfile(...) with optional overrides
✅ ASP.NET Core Pipeline Hook - .WithAspNetCoreOutputCaching(...) for MVC/Blazor/minimal output cache store
✅ Failover Affinity Hints - .WithFailoverAffinityHints(...) for cluster/web-garden sticky-session routing
✅ Kitchen Sink Profile - .WithKitchenSink(...) or AddVapeCacheKitchenSink(...) for one-call full integration
✅ Production Observability Profile - .WithProductionObservability(...) or AddVapeCacheWithProductionObservability(...)
✅ Low Ceremony - Single fluent chain to enable all major features
Installation
dotnet add package VapeCache.Extensions.Aspire
Quick Start
1. AppHost (Aspire Orchestrator)
var builder = DistributedApplication.CreateBuilder(args);
// Add Redis resource
var redis = builder.AddRedis("redis");
// Add your API with Redis reference
var api = builder.AddProject<Projects.MyApi>("api")
.WithReference(redis); // Injects connection string
builder.Build().Run();
2. API Project (Your Application)
var builder = WebApplication.CreateBuilder(args);
// Production baseline: observability on, app-hosted debug/admin endpoint surface remains opt-in.
var vapeCache = builder.AddVapeCache()
.WithRedisFromAspire("redis")
.UseTransport(VapeCacheAspireTransportMode.Balanced)
.WithCacheStampedeProfile(CacheStampedeProfile.Balanced)
.WithProductionObservability();
// Optional app-hosted diagnostics surface (protect with auth/network policy):
vapeCache.WithAutoMappedEndpoints(options =>
{
options.Enabled = true;
options.EnableDashboard = true;
options.EnableLiveStream = true;
});
var app = builder.Build();
app.UseVapeCacheOutputCaching();
app.UseVapeCacheFailoverAffinityHints();
app.MapHealthChecks("/health");
app.Run();
3. Use the Cache
public class UserService
{
private readonly ICacheService _cache;
public UserService(ICacheService cache) => _cache = cache;
public async Task<User?> GetUserAsync(int id, CancellationToken ct)
{
var key = $"user:{id}";
return await _cache.GetOrSetAsync(
key,
async ct => await _db.Users.FindAsync(id, ct),
(writer, user) => JsonSerializer.Serialize(writer, user),
bytes => JsonSerializer.Deserialize<User>(bytes),
new CacheEntryOptions(
Ttl: TimeSpan.FromMinutes(5),
Intent: new CacheIntent(CacheIntentKind.ReadThrough, Reason: "user detail lookup")),
ct);
}
}
Aspire Dashboard
Navigate to http://localhost:15888 to view:
Metrics
cache.current.backend- Current active backend (1=redis, 0=in-memory, -1=unknown) - Real-time visibilitycache.get.hits- Cache hits (by backend: redis, in-memory, hybrid)cache.get.misses- Cache missescache.fallback.to_memory- Circuit breaker fallback eventscache.set.payload.bytes- Payload size histogram for writes (large-key visibility)cache.set.large_key- Large payload writes (>64 KB)cache.evictions- In-memory eviction count (tagged by eviction reason)cache.op.ms- Operation latencyredis.cmd.calls- Redis commands executedredis.pool.wait.ms- Connection pool wait timeredis.mux.lane.inflight- Current in-flight operations by lane (connection.idtag)redis.mux.lane.inflight.utilization- In-flight utilization ratio by laneredis.mux.lane.bytes.sent- Cumulative bytes sent by laneredis.mux.lane.bytes.received- Cumulative bytes received by laneredis.mux.lane.operations- Cumulative operations started by laneredis.mux.lane.failures- Cumulative transport/connect failures by laneefcore.cache.query.execution.completed- EF Core query executions observed by interceptor cache pipelineefcore.cache.query.execution.failed- EF Core query execution failures observed by interceptor cache pipelineefcore.cache.query.execution.ms- EF Core query execution duration histogramefcore.cache.invalidation.zone.invalidated- EF Core-derived zone invalidations completedefcore.cache.invalidation.zone.failed- EF Core-derived zone invalidations failed
Traces
End-to-end distributed traces showing:
- Cache operation → Pool acquisition → Redis command → Response parsing
- EF Core second-level cache events when
VapeCache.Extensions.EntityFrameworkCore.OpenTelemetryis installed
Health Checks
- redis: Redis connectivity (PING validation)
- vapecache: Circuit breaker state and cache statistics
API Reference
AddVapeCache()
Registers core VapeCache services.
builder.AddVapeCache()
AddVapeCacheKitchenSink(configure?)
Adds core VapeCache services and applies a composed Aspire profile in one call:
builder.AddVapeCacheKitchenSink(options =>
{
options.RedisConnectionName = "redis";
options.ConfigureEndpoints = endpoint => endpoint.Enabled = true;
});
This composes:
WithRedisFromAspire(...)UseTransport(...)WithCacheStampedeProfile(...)WithHealthChecks()WithAspireTelemetry()WithStartupWarmup()WithAspNetCoreOutputCaching()WithFailoverAffinityHints()WithAutoMappedEndpoints(...)
AddVapeCacheWithProductionObservability(configure?)
Adds core VapeCache services and wires production observability defaults:
builder.AddVapeCacheWithProductionObservability(options =>
{
options.EnableStartupWarmup = true; // optional
});
Default composition:
WithHealthChecks()WithAspireTelemetry()
Optional composition:
WithStartupWarmup(...)WithRedisExporterMetrics(...)
WithRedisFromAspire(connectionName)
Uses the AppHost resource name so connection details come from Aspire service discovery.
.WithRedisFromAspire("redis") // Matches AppHost resource name
WithHealthChecks()
Adds health checks for Redis and VapeCache.
.WithHealthChecks()
// Map in your host:
app.MapHealthChecks("/health");
app.MapHealthChecks("/health/redis", new HealthCheckOptions
{
Predicate = check => check.Name == "redis"
});
WithAspireTelemetry()
Configures OpenTelemetry for VapeCache metrics/traces and OTLP export.
Also registers the EF Core cache meter/source (VapeCache.EFCore.Cache) so EF Core cache telemetry is auto-collected when the EF Core OTEL package is installed.
Resolution order for OTLP endpoint:
options.OtlpEndpointOpenTelemetry:Otlp:Endpoint(configuration)OTEL_EXPORTER_OTLP_ENDPOINT(environment)DOTNET_DASHBOARD_OTLP_ENDPOINT_URL(Aspire dashboard fallback)- Seq default:
http://localhost:5341/ingest/otlp
.WithAspireTelemetry()
WithKitchenSink(configure?)
Composes all major Aspire integration extensions for an existing builder chain:
builder.AddVapeCache()
.WithKitchenSink(options =>
{
options.RedisConnectionName = "redis";
options.EnableRedisExporterMetrics = true;
options.ConfigureEndpoints = endpoint =>
{
endpoint.Enabled = true;
endpoint.EnableLiveStream = true;
};
});
WithProductionObservability(configure?)
Production observability baseline without enabling app-hosted dashboard/admin route surfaces:
builder.AddVapeCache()
.WithProductionObservability(options =>
{
options.EnableStartupWarmup = true; // optional
options.EnableRedisExporterMetrics = false; // optional
});
WithCacheStampedeProfile(profile, configure?)
Applies named stampede defaults with optional fluent overrides.
.WithCacheStampedeProfile(
CacheStampedeProfile.Balanced,
options => options.WithLockWaitTimeout(TimeSpan.FromMilliseconds(600)));
WithAspNetCoreOutputCaching(configureOutputCache?, configureStore?)
Adds ASP.NET Core output caching and swaps the default store for VapeCacheOutputCacheStore.
builder.AddVapeCache()
.WithAspNetCoreOutputCaching(
configureOutputCache: options =>
{
options.AddBasePolicy(policy => policy.Expire(TimeSpan.FromSeconds(30)));
},
configureStore: store =>
{
store.KeyPrefix = "vapecache:output";
store.DefaultTtl = TimeSpan.FromSeconds(30);
store.EnableTagIndexing = true;
});
var app = builder.Build();
app.UseVapeCacheOutputCaching();
WithFailoverAffinityHints(configure?)
Adds options for middleware that emits node-affinity hints during failover:
builder.AddVapeCache()
.WithFailoverAffinityHints(options =>
{
options.NodeId = Environment.MachineName;
options.CookieName = "VapeCacheAffinity";
});
var app = builder.Build();
app.UseVapeCacheFailoverAffinityHints();
MapVapeCacheEndpoints(prefix, includeBreakerControlEndpoints, includeLiveStreamEndpoint, includeIntentEndpoints, includeDashboardEndpoint)
Maps wrapper-facing HTTP endpoints:
GET {prefix}/statusGET {prefix}/statsGET {prefix}/stream(Server-Sent Events realtime channel)GET {prefix}/dashboard(built-in realtime dashboard UI, optional)GET {prefix}/dashboard/dashboard.js(dashboard script)GET {prefix}/dashboard/dashboard.css(dashboard styles)- optional legacy:
POST {prefix}/breaker/force-open - optional legacy:
POST {prefix}/breaker/clear
Minimal API contract notes:
statusreturns backend state, cache stats, breaker state, and autoscaler diagnostics (when available).statsreturns cache counters + hit-rate + autoscaler diagnostics (when available).streamemits SSEevent: vapecache-statsframes with the live sample payload.- control endpoints are intentionally opt-in and should be mapped on an internal admin prefix.
var app = builder.Build();
app.MapVapeCacheEndpoints("/vapecache");
// Admin controls on an internal control-plane prefix:
app.MapVapeCacheAdminEndpoints("/internal/vapecache-admin");
MapVapeCacheAdminEndpoints(prefix = "/vapecache/admin", requireAuthorization = false, authorizationPolicy = null)
Maps admin-only breaker control endpoints:
POST {prefix}/breaker/force-openPOST {prefix}/breaker/clear
Set requireAuthorization: true to apply RequireAuthorization() in one line, or pass authorizationPolicy for RequireAuthorization(policy).
Keep this prefix internal-only.
app.MapVapeCacheAdminEndpoints(
prefix: "/internal/vapecache-admin",
requireAuthorization: true,
authorizationPolicy: "VapeCacheAdmin");
GET {prefix}/status and GET {prefix}/stats include the stampede hardening counters:
stampedeKeyRejectedstampedeLockWaitTimeoutstampedeFailureBackoffRejected
They also include autoscaler diagnostics when IRedisMultiplexerDiagnostics is available:
autoscaler.currentConnectionsautoscaler.targetConnectionsautoscaler.highSignalCountautoscaler.timeoutRatePerSecautoscaler.rollingP95LatencyMsautoscaler.rollingP99LatencyMsautoscaler.unhealthyConnectionsautoscaler.reconnectFailureRatePerSecautoscaler.scaleEventsInCurrentMinuteautoscaler.maxScaleEventsPerMinuteautoscaler.frozenautoscaler.freezeReasonautoscaler.lastScaleDirectionautoscaler.lastScaleReason
They also include lane diagnostics for graphing:
lanes[].laneIndexlanes[].connectionIdlanes[].role(read,write, orread-write)lanes[].writeQueueDepthlanes[].inFlightlanes[].maxInFlightlanes[].inFlightUtilizationlanes[].bytesSentlanes[].bytesReceivedlanes[].operationslanes[].failureslanes[].healthy
/stream emits event: vapecache-stats frames with a JSON payload compatible with Blazor realtime chart components.
Lane query/panel pack for Aspire Metrics explorer:
Example payload:
{
"timestampUtc": "2026-02-24T20:54:00.0000000+00:00",
"currentBackend": "redis",
"hits": 123456,
"misses": 7890,
"setCalls": 45678,
"removeCalls": 321,
"fallbackToMemory": 12,
"redisBreakerOpened": 2,
"stampedeKeyRejected": 0,
"stampedeLockWaitTimeout": 1,
"stampedeFailureBackoffRejected": 0,
"hitRate": 0.9399,
"autoscaler": {
"enabled": true,
"currentConnections": 6,
"targetConnections": 7,
"minConnections": 4,
"maxConnections": 16,
"currentReadLanes": 3,
"currentWriteLanes": 3,
"highSignalCount": 2,
"avgInflightUtilization": 0.81,
"avgQueueDepth": 7.4,
"maxQueueDepth": 34,
"timeoutRatePerSec": 0.0,
"rollingP95LatencyMs": 22.7,
"rollingP99LatencyMs": 34.0,
"unhealthyConnections": 0,
"reconnectFailureRatePerSec": 0.0,
"scaleEventsInCurrentMinute": 1,
"maxScaleEventsPerMinute": 2,
"frozen": false,
"frozenUntilUtc": null,
"freezeReason": null,
"lastScaleEventUtc": "2026-02-24T21:02:08.0000000+00:00",
"lastScaleDirection": "up",
"lastScaleReason": "inflight+queue"
},
"lanes": [
{
"laneIndex": 0,
"connectionId": 12,
"role": "read-write",
"writeQueueDepth": 1,
"inFlight": 22,
"maxInFlight": 128,
"inFlightUtilization": 0.171875,
"bytesSent": 8021569,
"bytesReceived": 14482991,
"operations": 54120,
"failures": 0,
"healthy": true
}
]
}
WithAutoMappedEndpoints(options => ...)
Registers a startup filter that maps VapeCache endpoints automatically so you don't need to wire them in Program.cs.
Endpoint mapping is disabled until options.Enabled = true.
builder.AddVapeCache()
.WithAutoMappedEndpoints(options =>
{
options.Enabled = true;
options.Prefix = "/cache";
options.AdminPrefix = "/internal/cache-admin";
options.IncludeBreakerControlEndpoints = true;
options.RequireAuthorizationOnAdminEndpoints = true;
options.AdminAuthorizationPolicy = "VapeCacheAdmin";
options.EnableLiveStream = true;
options.EnableDashboard = true;
options.LiveSampleInterval = TimeSpan.FromMilliseconds(500);
options.LiveChannelCapacity = 512;
});
When IncludeBreakerControlEndpoints is enabled, auto-mapping keeps Prefix read-only and maps control routes under AdminPrefix.
Dashboard UI frontend source is maintained in VapeCache.Extensions.Aspire/dashboard-ui (Vite + TypeScript).
To rebuild the shipped dashboard assets (DashboardAssets/):
cd VapeCache.Extensions.Aspire/dashboard-ui
npm install
npm run build
Enterprise transport/autoscaler architecture and tuning:
Custom Wrapper/Exporter Scenario
builder.AddVapeCache()
.WithAspireTelemetry(options =>
{
options.UseSeq(seqBaseUrl: "http://localhost:5341", apiKey: "dev-seq-key")
.WithOtlpHeader("x-env", "dev")
.AddMetricsConfiguration(m =>
{
// Add custom metric exporter extensions here
})
.AddTracingConfiguration(t =>
{
// Add custom trace exporter extensions here
});
});
Health Check Details
Redis Health Check (redis)
- Healthy: Redis is reachable and can execute commands
- Degraded: Connection pool timeout (under pressure)
- Unhealthy: Redis connection failed
VapeCache Health Check (vapecache)
- Healthy: Redis is the active backend and the circuit breaker is closed
- Degraded: Manual failover is enabled, the breaker is open, or the current backend is not Redis
- Unhealthy: The health probe itself failed while reading cache state or dependencies
Diagnostic Data:
{
"circuit_breaker_open": false,
"consecutive_failures": 0,
"hit_count": 12345,
"miss_count": 678,
"hit_rate": 0.948
}
Production Deployment
Azure Container Apps
azd up # Deploy via Aspire
Health checks are automatically configured for liveness/readiness probes.
Kubernetes
livenessProbe:
httpGet:
path: /health/redis
port: 8080
initialDelaySeconds: 10
periodSeconds: 30
readinessProbe:
httpGet:
path: /health
port: 8080
initialDelaySeconds: 5
periodSeconds: 10
License
Apache 2.0
Blazor Realtime Example
See docs/BLAZOR_DASHBOARD_EXAMPLE.md for a full Blazor component and stream client using:
GET /vapecache/stream(SSE realtime feed)GET /vapecache/status(snapshot fallback)
See Also
| Product | Versions Compatible and additional computed target framework versions. |
|---|---|
| .NET | net10.0 is compatible. net10.0-android was computed. net10.0-browser was computed. net10.0-ios was computed. net10.0-maccatalyst was computed. net10.0-macos was computed. net10.0-tvos was computed. net10.0-windows was computed. |
-
net10.0
- Autofac (>= 9.0.0)
- Microsoft.Extensions.ServiceDiscovery (>= 9.0.0)
- OpenTelemetry.Exporter.OpenTelemetryProtocol (>= 1.11.2)
- OpenTelemetry.Extensions.Hosting (>= 1.11.2)
- VapeCache.Extensions.AspNetCore (>= 1.2.12)
- VapeCache.Runtime (>= 1.2.12)
NuGet packages
This package is not used by any NuGet packages.
GitHub repositories
This package is not used by any popular GitHub repositories.
| Version | Downloads | Last Updated |
|---|---|---|
| 1.2.22 | 113 | 4/26/2026 |
| 1.2.21 | 107 | 4/26/2026 |
| 1.2.20 | 129 | 4/13/2026 |
| 1.2.19 | 100 | 4/12/2026 |
| 1.2.18 | 112 | 3/30/2026 |
| 1.2.17 | 102 | 3/27/2026 |
| 1.2.15 | 106 | 3/19/2026 |
| 1.2.14 | 107 | 3/18/2026 |
| 1.2.13 | 109 | 3/14/2026 |
| 1.2.12 | 110 | 3/14/2026 |
| 1.2.11 | 106 | 3/14/2026 |
| 1.2.10 | 108 | 3/13/2026 |
| 1.2.9 | 109 | 3/12/2026 |
| 1.2.8 | 105 | 3/12/2026 |
| 1.2.7 | 105 | 3/11/2026 |
| 1.2.6 | 104 | 3/11/2026 |
| 1.2.5 | 108 | 3/11/2026 |
| 1.2.4 | 119 | 3/10/2026 |
| 1.2.3 | 107 | 3/10/2026 |
| 1.2.2 | 105 | 3/10/2026 |
1.2.10 release: stabilized Redis telemetry parser metrics, hardened connection registration across DI and Autofac, and refreshed production-capable documentation.