-
Notifications
You must be signed in to change notification settings - Fork 0
Attributes
Aghogho Bernard edited this page May 12, 2026
·
2 revisions
Decorates an ASP.NET Core action to cache its response.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class CacheWeaveAttribute : Attribute// Explicit key
[CacheWeave("products:list")]
// Derived key — resolves to "ControllerName.ActionName" at runtime
[CacheWeave]| Property | Type | Default | Description |
|---|---|---|---|
Key |
string? |
null |
Base cache key. null = derive from controller/action |
ExpirySeconds |
int |
-1 |
TTL in seconds. -1 = use global default. 0 = no expiry |
IncludeRouteParams |
bool |
true |
Append sorted route params (path segments) to key. Framework keys (controller, action, page, area) are always excluded |
ExcludeRouteParams |
string[] |
[] |
Route param names to exclude from key |
IncludeQueryParams |
bool |
true |
Append sorted query params to key |
ExcludeParams |
string[] |
[] |
Query param names to exclude from key |
HashBody |
bool |
false |
Append SHA-256 body hash to key |
HashBodyFields |
string[] |
[] |
Hash only these fields (empty = entire body) |
SlidingExpiry |
bool |
false |
Reset TTL on every cache hit |
NoCacheWhen |
NoCacheCondition |
OnErrorOrEmpty |
Condition to skip caching |
// Basic — 5 minute TTL, include query params
[HttpGet]
[CacheWeave("products:list", ExpirySeconds = 300)]
public Task<IActionResult> GetAll([FromQuery] int page = 1) { ... }
// No expiry — cached indefinitely (until evicted)
[HttpGet("config")]
[CacheWeave("app:config", ExpirySeconds = 0)]
public Task<IActionResult> GetConfig() { ... }
// Route parameters — {id} is automatically included in the cache key
// GET /products/42 → key: "products:id=42"
[HttpGet("{id}")]
[CacheWeave("products", ExpirySeconds = 600)]
public Task<IActionResult> GetById(Guid id) { ... }
// Route params with exclusions
// GET /products/42/draft → key: "products:id=42" (version excluded)
[HttpGet("{id}/{version}")]
[CacheWeave("products", ExcludeRouteParams = ["version"])]
public Task<IActionResult> GetById(int id, string version) { ... }
// Disable route params — only base key is used
[HttpGet("{id}")]
[CacheWeave("products:stats", IncludeRouteParams = false)]
public Task<IActionResult> GetStats(int id) { ... }
// Sliding expiry — TTL resets on every hit
[HttpGet("{id}")]
[CacheWeave("products:detail", SlidingExpiry = true, ExpirySeconds = 600)]
public Task<IActionResult> GetById(Guid id) { ... }
// POST with body hash — different bodies get different cache entries
[HttpPost("search")]
[CacheWeave("products:search", HashBody = true, ExpirySeconds = 120)]
public Task<IActionResult> Search([FromBody] SearchRequest req) { ... }
// POST with selective field hash — ignore noise fields
[HttpPost("search")]
[CacheWeave("products:search",
HashBody = true,
HashBodyFields = ["term", "categoryId"],
ExpirySeconds = 120)]
public Task<IActionResult> Search([FromBody] SearchRequest req) { ... }
// Never cache errors, but cache empty results
[HttpGet]
[CacheWeave("products:list", NoCacheWhen = NoCacheCondition.OnError)]
public Task<IActionResult> GetAll() { ... }
// Always cache (even errors and empty results)
[HttpGet]
[CacheWeave("products:list", NoCacheWhen = NoCacheCondition.Never)]
public Task<IActionResult> GetAll() { ... }
// Derived key — no explicit key needed
[HttpGet]
[CacheWeave]
public Task<IActionResult> GetAll() { ... }
// → key: "Products.GetAll:v1:page=1"Evicts cache entries after an action executes successfully.
[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class CacheWeaveEvictAttribute : Attribute| Property | Type | Default | Description |
|---|---|---|---|
Key |
string? |
null |
Exact key to remove |
Prefix |
string? |
null |
Remove all keys with this prefix |
EvictOnFailure |
bool |
false |
Evict even when the action throws or returns an error |
Set either Key or Prefix — not both.
// Evict a single key
[HttpDelete("{id}")]
[CacheWeaveEvict(Key = "products:detail")]
public Task<IActionResult> Delete(Guid id) { ... }
// Evict all keys under a prefix (requires provider support — Redis, InMemory, SQLite)
[HttpPost]
[CacheWeaveEvict(Prefix = "products:")]
public Task<IActionResult> Create([FromBody] CreateProductCommand cmd) { ... }
// Multiple evictions on one action
[HttpPut("{id}")]
[CacheWeaveEvict(Prefix = "products:")]
[CacheWeaveEvict(Key = "dashboard:stats")]
[CacheWeaveEvict(Key = "dashboard:summary")]
public Task<IActionResult> Update(Guid id, [FromBody] UpdateProductCommand cmd) { ... }
// Evict even on failure (e.g. to force a re-fetch after a partial update)
[HttpPost("import")]
[CacheWeaveEvict(Prefix = "products:", EvictOnFailure = true)]
public Task<IActionResult> Import([FromBody] ImportRequest req) { ... }Eviction always happens after the action executes. The sequence is:
- Action executes
- CacheWeave checks if the action succeeded (no exception, 2xx status)
- If succeeded (or
EvictOnFailure = true), eviction runs - Response is returned to the client
[Flags]
public enum NoCacheCondition
{
Never = 0, // always cache
OnError = 1 << 0, // skip on non-2xx status
OnEmpty = 1 << 1, // skip on null/empty body
OnErrorOrEmpty = OnError | OnEmpty // default
}Combine flags:
// Skip caching on errors OR empty results (same as default)
[CacheWeave("k", NoCacheWhen = NoCacheCondition.OnError | NoCacheCondition.OnEmpty)]