PageModel is the ASP.NET Core base class for a Razor Page’s page model. The page model handles requests and supplies state to the page’s Razor markup. A page selects its model with @model; expressions such as @Model.Product read properties on that model, not an object returned by an MVC action. section 23.2.2 section 23.4
A request through a Razor Page follows a useful sequence:
- Routing selects the page, normally from the .cshtml file’s location under Pages. ASP.NET Core creates a new page model instance and resolves its constructor dependencies through dependency injection. section 23.2.2
- A handler named for the HTTP method runs: OnGet or OnPost, with Async variants for asynchronous handlers. Model binding supplies handler parameters from the request. section 23.2.2 section 23.4.3
- The handler can set properties for the Razor markup or return a result. After a successful POST, RedirectToPage() can prompt a GET so a browser reload does not repeat the POST. section 23.2.2 section 23.4.3
Put request work in the model
In Pages/Editor.cshtml.cs, EditorModel : PageModel receives a DataContext through its constructor. OnGetAsync(long id) loads a Product into a property displayed by Editor.cshtml. OnPostAsync(long id, decimal price) loads the product, changes its price, saves, and returns RedirectToPage(). The id comes from the route; price comes from the submitted form. section 23.4.3
Listing 23.17 The contents of the Editor.cshtml.cs file in the Pages folder
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;
using WebApp.Models;
namespace WebApp.Pages {
public class EditorModel : PageModel {
private DataContext context;
public Product? Product { get; set; }
public EditorModel(DataContext ctx) {
context = ctx;
}
public async Task OnGetAsync(long id) {
Product = await context.Products.FindAsync(id);
}
public async Task<IActionResult> OnPostAsync(long id,
decimal price) {
Product? p = await context.Products.FindAsync(id);
if (p != null) {
p.Price = price;
}
await context.SaveChangesAsync();
return RedirectToPage();
}
}
}
The paired handler methods show how one page model separates display and update requests. section 23.4.3
PageModel also exposes request context through properties including HttpContext, Request, Response, RouteData, and ModelState. For validation errors, use the Razor Page property path: ModelState.AddModelError("Product.Price", ...) matches @Model.Product.Price; "Price" alone does not. Named handlers offer another choice within one HTTP method: a handler value of related selects OnGetRelatedAsync. section 23.4 section 29.4 section 23.4.4
Use a separate .cshtml.cs class when you want to unit test handlers; a page model can also live in @functions within the .cshtml file. In a cart page, OnPost updates a session-backed cart and redirects, while OnGet loads the cart for display. Keep a page model when it does real request work. If it merely passes a service to the markup, @inject can remove it—but use that shortcut sparingly. section 8.2.5 section 23.6 section 23.5.3

