Separating concerns using Razor Pages

May 1, 2018 · 3 minute read · Tags: razorpages

Razor Pages (not to be confused with Razor views when you’re using MVC) are a handy option for creating simple pages in your ASP.NET Core app.

Create a new Razor Page in Visual Studio and you get the Razor (markup) page (.cshtml) and a corresponding model class (.cs);

From here, you can expose properties on your model class for data that you want to display on the page.

Here’s a simple example of a Kanban board list page (based on the app we build in Practical ASP.NET Core MVC).

public class ListModel : PageModel
{
    [BindProperty]
    public BoardListModel Data { get; set; }

    public void OnGet()
    {
        // get your data here
    }

}

Then in your Razor markup you can bind to this model.

@page
@model Donatello.Pages.Board.ListModel

<h2>Board List</h2>

@foreach (var board in Model.Data.Boards) 
{
    @board.Name
}

Data is an arbitrary choice of name, the properties on your Model can be called anything you like.

One early concern about Razor Pages is that it brings us full circle, back to WebForms (or even “classic” ASP) and will inevitably result in everyone lumping all their code together in one place (bye bye separation of concerns).

In reality, it’s actually a bit simpler than that.

Razor Pages simplifies a few things like routing (the route is simply the folder path and the name of the file e.g. /board/list) but otherwise operates as a kind of controllerless MVC action.

Rather than route to a controller (and action), navigating to /board/list takes you to the relevant OnGet() method for that Razor Page.

Responsibility for writing properly separated code lives (as it always has) with you, the developer.

So whilst you could lump all your code together in the Razor Page .cs file.

public class ListModel : PageModel
{

    [BindProperty]
    public BoardListModel Data { get; set; }

    public void OnGet()
    {
        Data = new BoardListModel
        {
            Boards = new List<BoardListModel.Board>
            {
                new BoardListModel.Board
                {
                    Id = Guid.NewGuid(),
                    Name = "A board"
                }
            }
        };
    }

}

For the sake of this example we’re just using hardcoded data but in real life would call a database or some such.

You could equally use dependency injection or any other mechanism you fancy to keep this Model lean.

public class ListModel : PageModel
{
    IBoardService _service;

    public ListModel(IBoardService service)
    {
        _service = service;
    }

    [BindProperty]
    public BoardListModel Data { get; set; }

    public void OnGet()
    {
        Data = _service.ListAll();
    }

}

Or, if you’re a MediatR fan (like me)…

public class ListModel : PageModel
{
    IMediator _mediator;

    public ListModel(IMediator mediator)
    {
        _mediator = mediator;
    }

    [BindProperty]
    public BoardListModel Data { get; set; }

    public async Task OnGetAsync()
    {
        Data = _mediator.Send(new ListAllQuery());
    }

}

However you want to do it, you can still push your main business code/logic somewhere else and let your Razor Pages do what they’re good at, exposing data for your Razor markup to bind to.

Oh and just before you go, lest you think this whole controllerless action idea is new, here’s an interesting article from 2009.

Join the Practical ASP.NET Newsletter

Ship better Blazor apps, faster. One practical tip every Tuesday.

I respect your email privacy. Unsubscribe with one click.

    Next up

    MVC vs Razor Pages - A quick comparison
    If you’re learning ASP.NET in this brave new .NET Core world and you want to build server-side web applications then it’s a straight fight between MVC and Razor Pages.