-
Notifications
You must be signed in to change notification settings - Fork 0
Cache Keys
CacheWeave assembles cache keys from multiple segments in a deterministic order. Understanding this lets you predict exactly what key will be used for any request.
{baseKey}{sep}{version?}{sep}{contextSegment?}{sep}{routeParams?}{sep}{queryParams?}{sep}{bodyHash?}
| Segment | Source | Example |
|---|---|---|
| Base key |
[CacheWeave("my-key")] or derived from controller/action |
products:list |
| Version | CacheWeaveOptions.KeyVersion |
v2 |
| Context | IKeyContextProvider.GetContextSegmentAsync() |
tenant=acme |
| Route params | Sorted route values from URL path (e.g. {id}) |
id=42 |
| Query params | Sorted, filtered query string | page=1:pageSize=20 |
| Body hash | SHA-256 of request body (or selected fields) | a3f9c2d1... |
GET /api/products/42/reviews?page=2
→ products:reviews:v2:tenant=acme:id=42:page=2
[CacheWeave("products:list")]The string "products:list" is used verbatim as the first segment.
[CacheWeave] // no key argumentCacheWeave uses the ControllerActionDescriptor to derive:
{ControllerName}.{ActionName}
Examples:
| Controller | Action | Derived Key |
|---|---|---|
ProductsController |
GetAll |
Products.GetAll |
MaterialCategoriesController |
GetById |
MaterialCategories.GetById |
ClientMaterialsController |
GetStatistics |
ClientMaterials.GetStatistics |
Set CacheWeaveOptions.KeyVersion to inject a version after the base key:
options.KeyVersion = "v2";
// "products:list" → "products:list:v2"Invalidation strategy: bump the version in config/environment variables to instantly invalidate all cached entries across all instances without touching Redis.
Implement IKeyContextProvider to inject a per-request segment:
public class TenantKeyContextProvider : IKeyContextProvider
{
private readonly IHttpContextAccessor _accessor;
public TenantKeyContextProvider(IHttpContextAccessor accessor)
=> _accessor = accessor;
public Task<string?> GetContextSegmentAsync(HttpContext context)
{
var tenantId = context.User.FindFirst("tenant_id")?.Value;
return Task.FromResult(tenantId is not null ? $"tenant={tenantId}" : null);
}
}Register it:
services.AddHttpContextAccessor();
services.AddSingleton<IKeyContextProvider, TenantKeyContextProvider>();Result:
products:list:v1:tenant=acme:page=1
When IncludeRouteParams = true (default), route values from the URL path are sorted alphabetically and appended to the cache key. Framework-owned route values (controller, action, page, area) are always excluded automatically.
GET /api/products/42
→ key: products:id=42
Without route params in the key, endpoints like [HttpGet("{id}")] would produce the same cache key for /products/1 and /products/2, returning incorrect cached responses.
Strip route values that don't affect the response:
[HttpGet("{id}/{version}")]
[CacheWeave("products", ExcludeRouteParams = ["version"])]
public Task<IActionResult> GetById(int id, string version) { ... }
// GET /products/42/draft → key: "products:id=42" (version excluded)[CacheWeave("products:stats", IncludeRouteParams = false)]Route params and query params are independent and stored in different places on the HttpContext:
| Route params | Query params | |
|---|---|---|
| URL location | In the path: /products/42
|
After ?: /products?id=42
|
| ASP.NET source | HttpContext.Request.RouteValues |
HttpContext.Request.Query |
| CacheWeave toggle | IncludeRouteParams |
IncludeQueryParams |
| Exclusion list | ExcludeRouteParams |
ExcludeParams |
Both can appear in the same key:
GET /api/products/42/reviews?page=2&sort=newest
→ key: products:reviews:id=42:page=2:sort=newest
^^^^^ ^^^^^^^^^^^^^^^^^^^^
route param query params
When IncludeQueryParams = true (default), all query parameters are sorted alphabetically and appended:
GET /api/products?pageSize=20&page=1&category=steel
→ key: products:list:category=steel:page=1:pageSize=20
Strip tracking IDs, debug flags, or correlation headers that don't affect the response:
[CacheWeave("products:list",
IncludeQueryParams = true,
ExcludeParams = ["trackingId", "correlationId", "debug"])][CacheWeave("products:stats", IncludeQueryParams = false)]For POST endpoints where the request body determines the response, append a SHA-256 hash:
[HttpPost("search")]
[CacheWeave("products:search", HashBody = true)]
public async Task<IActionResult> Search([FromBody] SearchRequest request) { ... }Key: products:search:a3f9c2d1e4b7...
If the body contains noise fields (request IDs, timestamps) that don't affect the result, hash only the stable fields:
[CacheWeave("products:search",
HashBody = true,
HashBodyFields = ["term", "categoryId", "filters"])]Only the listed fields are extracted and hashed — the rest are ignored.
Change the separator globally:
options.KeySeparator = ".";
// "products.list.v1.page=1"The default ":" is recommended for Redis as it enables namespace-based key browsing in RedisInsight and similar tools.