ASP.NET Core 2.0 Razor Pages – When I Grow up I Want to Be MVVM
20 September 2017

Update from 2021:
End of this article: “I wonder if there is a greater plan here… Would it be awesome to introduce observables here and automatically generate client code to handle notifications and changes?”.
My guess was right. In 2018 Blazor was released.


With ASP.NET Core 2.0 we got Razor Pages. I can only guess that this is only the first step of making something smarter then just “let’s go back to the time when we encouraged people to shove data, logic and DB access to one code-behind file”. What if this is just beginning and the next step is introduction of observables and automatic propagation of changes like in real MVVM?

Not real MVVM

Today Razor page looks like MVVM but it doesn’t 100% behave like one. MVVM is designed to work with observer pattern where changes are automatically propagated thru notifications. View Model contains properties and logic that is changing that “dynamic data”. That is the primary reason why it is OK to have data and logic merged in one place.

Good old MVC

As MVVM works great for the client side, MVC was the go-to pattern for the server side. In MVC we have clear separation of concerns. Model is “just data” and controller is the one who grabs data from services before exchanging it with the view. With all greatness of ASP MVC model binding we have a great help in mapping HTTP request to the model.

Why Razor Pages

MVC has everything including binding. Why would you wish for Razor Pages?

There are two scenarios. Maybe you struggle to understand MVC architecture and you are confused with all that “ceremony” of creating controllers and models. Or you have simple case where there is not much server side logic and controller is really dumb. For the second scenario it is probably OK to merge data and some logic. With Razor Pages we lost Controller and Model and got one new thing called Page Model sitting in code-behind file. You can also skip code-behind file and stick Page Model in @function block at the Razor page itself.

How binding looks like?

Let’s create one super simple example to show new binding.

Razor file must start with @page directive to tell MVC to back off and treat it like Razor Page with code-behind file.

@page

@model IndexPageModel

<h4>@Model.Message</h4>
<div class="row">
<div class="col-md-4">
<form method="post">
<div class="form-group">
<label asp-for="Title" class="control-label"></label>
<input asp-for="Title" class="form-control" />
</div>
<div class="form-group">
<label asp-for="Year" class="control-label"></label>
<input asp-for="Year" class="form-control" />
</div>
<div class="form-group">
<input type="submit" value="Save" class="btn btn-default" />
</div>
</form>
</div>
</div>

Razor code above is using 3 properties from the model. Message, Title and Year. Let’s write Page Model for this page.

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace CenterForDevelopers.Pages
{
public class IndexPageModel : PageModel
{
public string Message { get; set; }

[BindProperty] // => opt-in to model binding
public int Year { get; set; }
[BindProperty] // => opt-in to model binding
public string Title { get; set; }

public IndexPageModel()
{
}

public void OnGet()
{
(Year, Title) = GetDataFromTheService();

BuildMessage("Get");
}

public PageResult OnPost()
{
BuildMessage("Post");

// Send data to the service

return Page();
}

private void BuildMessage(string action)
{
Message = $"{action} for '{Title}' ({Year})";
}

private (int Year, string Title) GetDataFromTheService()
{
return (1976, "Rocky");
}
}
}

Since your Page Model now can have tons of other properties (it is more then just data) we must use [BindProperty] attribute to opt-in for model binding. Try removing this attribute and you will observe that although GET will still work, POST will fail to bind form field to the model (for example you can get rid of [BindProperty] attribute for Year and observe difference in behavior comparing to Title).

What is next?

For some simple pages having mix of data and functions is OK but it is messy. I wonder if there is a greater plan here or this move was just to attract more developers especially the ones who can get easily “confused” with MVC? Would it be awesome to introduce observables here and automatically generate client code to handle notifications and changes? In that case Page Model would make perfect sense!