Skip to content

Attributes

Aghogho Bernard edited this page May 12, 2026 · 2 revisions

Attributes

[CacheWeave]

Decorates an ASP.NET Core action to cache its response.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = false)]
public sealed class CacheWeaveAttribute : Attribute

Constructor Overloads

// Explicit key
[CacheWeave("products:list")]

// Derived key — resolves to "ControllerName.ActionName" at runtime
[CacheWeave]

Properties

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

Examples

// 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"

[CacheWeaveEvict]

Evicts cache entries after an action executes successfully.

[AttributeUsage(AttributeTargets.Method, AllowMultiple = true)]
public sealed class CacheWeaveEvictAttribute : Attribute

Properties

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.

Examples

// 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 Timing

Eviction always happens after the action executes. The sequence is:

  1. Action executes
  2. CacheWeave checks if the action succeeded (no exception, 2xx status)
  3. If succeeded (or EvictOnFailure = true), eviction runs
  4. Response is returned to the client

NoCacheCondition Flags

[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)]

Clone this wiki locally