-
Notifications
You must be signed in to change notification settings - Fork 0
Razor Pages
CacheWeave supports Razor Pages via CacheWeavePageFilter, an IAsyncPageFilter.
Register CacheWeavePageFilter as a global page filter via AddMvcOptions:
builder.Services.AddCacheWeave(options => { ... });
builder.Services.AddRazorPages()
.AddMvcOptions(options => options.Filters.AddService<CacheWeavePageFilter>());Apply [CacheWeave] to OnGet / OnGetAsync handlers:
using CacheWeave.Core;
public class ProductsModel : PageModel
{
private readonly IMediator _mediator;
public ProductsModel(IMediator mediator) => _mediator = mediator;
public IReadOnlyList<ProductDto> Products { get; private set; } = [];
[CacheWeave("razor:products:list", ExpirySeconds = 300, IncludeQueryParams = true)]
public async Task<IActionResult> OnGetAsync(
[FromQuery] int page = 1,
[FromQuery] int pageSize = 20)
{
var result = await _mediator.Send(new GetProductsQuery { Page = page, PageSize = pageSize });
Products = result.Items;
return Page();
}
}Apply [CacheWeaveEvict] to mutation handlers:
public class CreateProductModel : PageModel
{
[BindProperty]
public CreateProductInputModel Input { get; set; } = new();
[CacheWeaveEvict(Prefix = "razor:products:")]
public async Task<IActionResult> OnPostAsync()
{
if (!ModelState.IsValid) return Page();
await _mediator.Send(new CreateProductCommand(Input));
return RedirectToPage("./Index");
}
}Omit the key argument and CacheWeave derives it from the page name and handler:
[CacheWeave] // key: "Products.OnGetAsync" (or similar derived name)
public async Task<IActionResult> OnGetAsync() { ... }The derived key format for Razor Pages uses the ActionDescriptor.DisplayName since there is no ControllerActionDescriptor. The exact value depends on the page route — use an explicit key for predictability.
Route parameters in Razor Pages (e.g. @page "{id}") are automatically included in the cache key by default. Framework route values (controller, action, page, area) are always excluded.
// Pages/Products/Detail.cshtml — @page "{id:int}"
public class DetailModel : PageModel
{
public ProductDto Product { get; private set; } = default!;
// GET /Products/Detail/42 → key: "razor:products:detail:id=42"
[CacheWeave("razor:products:detail", ExpirySeconds = 600)]
public async Task<IActionResult> OnGetAsync(int id)
{
Product = await _mediator.Send(new GetProductByIdQuery(id));
return Page();
}
}// GET /Products/Detail/42/draft → key: "razor:products:detail:id=42" (version excluded)
[CacheWeave("razor:products:detail", ExcludeRouteParams = ["version"])]
public async Task<IActionResult> OnGetAsync(int id, string version) { ... }[CacheWeave("razor:products:stats", IncludeRouteParams = false)]
public async Task<IActionResult> OnGetAsync(int id) { ... }CacheWeavePageFilter operates at the handler level, not the view level. To cache partial views or view components, use ICacheWeaveService directly in the view component:
public class ProductStatsViewComponent(ICacheWeaveService cache) : ViewComponent
{
public async Task<IViewComponentResult> InvokeAsync()
{
var stats = await cache.GetOrSetAsync<ProductStatsDto>(
"products:stats",
factory: async () => await _db.Products
.GroupBy(_ => 1)
.Select(g => new ProductStatsDto
{
Total = g.Count(),
Active = g.Count(p => p.IsActive)
})
.FirstOrDefaultAsync(),
expiry: TimeSpan.FromMinutes(10));
return View(stats);
}
}-
HashBody = trueis not meaningful for GET handlers (no request body). UseIncludeQueryParams = trueinstead. - Sliding expiry works the same as in MVC controllers — the entry is re-written on every hit.
-
[CacheWeaveEvict]on a POST handler evicts after the handler executes and before the redirect response is sent.