The Struggles of Using ASP.NET Razor Pages
By: Brock Joseph Herion
Tags: C Sharp Programming, Web Design, Web Development, Dotnet Core, Programming

Sometimes, it’s just easier to do things in Javascript

Image by Olalekan Oladipupo from Pixabay

As a full stack web developer, I work in a lot of different languages, frameworks, and technologies. There are things I absolutely love using and things I try to avoid using at all costs. I adore C#, Python, and Javascript for my backends while I would rather not touch PHP or Java. Out of all of these, C# is my most favorite to work in. It’s fast, powerful, easy to write in, and is cross platform thanks to .NET Core. Microsoft has done a wonderful job developing the .NET platform in recent years.

While I can talk about how great C# is all day, I cannot say the same thing for Razor Pages. I am not talking about Blazor, which is the .NET Core way to build Web Assembly apps. I am talking about Razor Pages and using them as a front end for an application. I can’t talk much about Blazor because I haven’t used it, but I can say that in my searching for solutions about Razor I have found how to do the equivalent in Blazor.

The Razor Breakdown

Razor, to me, is a huge step up from WebForms. They are similar enough to be easy for a WebForms developer to pick up, but much more flexible and power. Being able to integrate C# in the front end is wonderful and makes working with a C# backend much easier, as you can share models and classes between them. If I wanted to create a table, I could do it very easily like this

<table>

<tr>

@for (var user in Model.Users) {

<td>@user.FirstName</td>

<td>@user.LastName</td>

<td>@user.Email</td>

...

}

</tr>

</table>

If you think it looks similar to something like Jinja or Thymeleaf, you would be right, but there are a few fundamental differences that make Razor much more powerful. Notice in the code about I’m writing a standard C# for loop and in that loop I am iterating over a collection called Users. Also notice the Model in front of Users. The Model of a Razor page is a POCO, either a database entity or a class that extends from PageModel, similar to how WebForms worked with its code-behind. When I am creating the table, I can build each row by pulling data from the model directly, not need to do any JSON parsing or data converting.

On the surface, Razor is super powerful and I much prefer it’s syntax and ease of use over tools like Django templates. That being said, I recently hit some hard limits with it that do indeed have workarounds, but finding them is extremely different. These are issues that a frontend like React or Angular would fix very easily, but I will get to those later.

Complications Arise

The first problem I ran into with Razor is trying to do anything more than a basic form. Razor has built in syntax for binding inputs, labels, and validation to models, so you can do all that in C#. When you post from a form, Razor will try and find an OnPost method in the code-behind. From that method, you can do whatever you want with the data you received. Again, it works great with basic forms and input fields, but falls apart once they get more complicated.

Multiple POST Requests

The first problem was handing multiple POST requests. This issue was not the end of the world, but finding a solution was much more difficult that I thought. You see, there are Blazor and Razor MVC docs for it, but nothing about using Razor as a front end alone. I eventually found the solution in a blog post, which turned out to be as easy as adding an

asp-page-handler="SomeFunction"

to the submit input. Not bad, and very easy to implement. Finding the solution however, was not easy and introduced a new can of worms as the form grew. Some new issues that popped up were

The entire page would do a post back, validating other forms on the page. Not that big of a deal if that’s your intended behavior, but very frustrating if your forms in a sequence or optional.

Because of the post back, the page would then load back at the top of the page, causing you to have to scroll back to where you were.

So far, these issues were minor and was manageable, until I started adding Lists to my model. On my form, I have a model with normal input fields and now needed a separate form to handle adding items to this list. This is where the fun began!

AJAX, Lists, and State oh my!

The first issue was dealing with the two problems listed above. When submitting a new element to be added to the list, it would validate the entire page. You could not submit anything without the other parts of the page validating. To fix this, I moved the list additions out into its own form and added a Javascript onclick event to it. I was then able to get the form fields and use a JQuery AJAX call back to the handler in the code-behind. This worked, until some unintentional side effects came up. Adding a new element to the list would add the element, but adding anything after the first one would set the first element to null, breaking any loops I had in my Razor syntax and crashing my program. It would also break any dropdown lists on the page and cause them to become unbound from any lists they were using.

The solution was to store the list in the Session. To add an element now, I simply grab the list and add then user and save it back to the session. At this point, I still have two different forms for the page, one for the normal input fields and and one to hold the fields I need for models in my list. The list form was being handled with AJAX requests and the other form with the asp-page-handler to handle submits. Another problem here arose.

When trying to submit both forms to save the model and the list, the model would have all its fields wiped. The solution, this one simple, was to combine both forms. I now have all my fields from my model and all the elements in my list. The code itself now is rather messy. I have some buttons that directly post back to the server while others are being handled with Javascript and AJAX requests.

Components

Now my form has fields and blocks that are shared between forms. What can I do? Copy and paste it between pages. Razor pages do indeed support components, but they are for rendering views only. Blazor, as far as I can tell, supports a true component system, but Razor does not.

The issue came up when needing to reuse my list form on multiple pages. I needed the exact same form and exact same list in three different forms. When using components in Razor, you can pass POCO’s into the component and render it out to your page. It’s neat, except it doesn’t solve the issue of getting data out of the form. It only renders the objects passed in. So technically, I can use a view component so I can render my list, but I cannot use it to reuse parts of the form.

Is Razor Bad?

Given my complaints above, you might think I dislike Razor, but it’s quite the contrary. I like Razor pages. I think they are one of the most versatile MVVM/MVC templates you can use right now. I love that the learning curve is so small and that you can write actual C# code in your webpages. I also like how easy it is to bind models to pages. But for a site with a complicated form workflow, I would stick with a Javascript framework.

Using something like React or Angular in my situation would have made the process much smoother. My solution for both frameworks would be almost the same structurally.

Create some sort of state manager in RXJS (Angular) or use Redux/NGRX to handled state in the Redux pattern. I would initialize an empty form state the entire form would be tied to.

Create the page that needs the form on it.

Create different components that the form would need and need to be shared across multiple forms.

Bind the various state objects to its corresponding form and pass in handler methods to the components that would update the state when the form is changed.

When the form is completed, send the form whatever backend you are using with an HTTP POST request.

At the end of the day, the Razor solution did turn out somewhat similar to the Javascript framework version, minus the components. I did create a form of state management using session storage which does work. My biggest gripe with this is not having the ability to reuse components in the same way you can in React or Angular and having duplicated code. My other gripe is that the Microsoft docs weren’t exactly the most helpful in addressing these issues. Hours of browsing StackOverflow and testing out various solutions only to run into dead ends was really frustrating. So while Razor is not bad by any means, it certainly has it’s limitations.

Conclusion

As I said, I don’t think Razor is bad by any means. To me, it’s much more powerful and flexible than things Django templates or Java Thymeleaf. Being able to write your C# on the front-end and back-end is a massive benefit to a development team. But sometimes, doing a front-end in React or Angular is just easier. Right now, the Javascript ecosystem is more mature and more powerful than Razor and provides much more flexibility than Razor. Not only are there better state management tools like Redux, better code reusability, and a larger variety of deployment options. That being said, if you’re primarily a back-end developer or you don’t need the kinds of tooling and flexibility that the Javascript ecosystem offers, then Razor pages might be a great solution, especially if you come from a C# background.