Chapter 2
IN THIS CHAPTER
Understanding how Razor fits into the picture
Working with variables in Razor
Making decisions using Razor
Storing and cycling through data
As mentioned in Chapter 1 of this minibook, Razor is Microsoft’s language for implementing dynamic web content. While Chapter 1 shows how to create static content, most websites today use dynamic content so that updating them is easier. Using Razor is sort of like mixing C#, HTML, and some older technologies reminiscent of Active Server Pages (ASP) (https://docs.microsoft.com/en-us/troubleshoot/iis/asp-support-windows
). The first part of this chapter discusses how this mix occurs and shows you some basics.
As with C#, any discussion of Razor needs to begin with using variables to store data. This means understanding the Razor data types, how to work with operators, and how to convert one data type to another. The information you obtained in Book 1, “The Basics of C# Sharp Programming,” and especially in Chapters 2 through 4, will help you in this minibook.
The next three parts of the chapter discuss how to work with and manipulate data using a combination of logical statements, arrays, and loops. If you’ve read through Chapters 5 through 7 of Book 1, you’ll find the information in these three parts of the chapter quite familiar because Razor is based on C# and follows the C# rules. However, there are differences between Razor and C# that these parts of the chapter help point out.
Trying to comprehend a new technology can be difficult, and the learning curve often feels artificially steep. In fact, it feels like those early days of shaving when being unfamiliar with the razor makes one nick their skin constantly. The following sections help you avoid the nicks by bringing the learning curve down a little. To make this process easier, create an ASP.NET Core Web App using the template wizard and insights contained in the “Creating a basic app using the ASP.NET Core Web App” section of Chapter 1 of this minibook. You don't need to add anything to the resulting project; it’s just helpful to have the files available for viewing. Use the .NET 3.1 (Long-term Support) framework and select None for Authentication type; deselect the Configure for HTTPS option, and select the Enable Razor runtime-compilation option. As an alternative, you can use the RazorPageOverview
example found in the downloadable source.
There is a long list of technologies that precede Razor, representing a sort of a flavor-of-the-year march toward where the technology sits today. It starts with classic Active Server Pages (ASP), moves through various incarnations of that technology, through Model-View-Controller (MVC), to Razor today. If you’ve been a developer for long, the subtle but continuing changes in technology could drive you a bit nuts. In fact, when you go online to various tutorial sites such as W3Schools (https://www.w3schools.com/asp/default.ASP
), even they have a hard time condensing the technology and making it easier to understand. When you start looking at ASP.NET tutorials (https://www.tutorialspoint.com/asp.net/asp.net_first_example.htm
), you begin to see bits and pieces of what eventually became Razor. For example, both use the @page
directive (look at the top of Index.cshtml
in your project to see this directive), but Razor uses it in a slightly different manner, which is the source of much of the confusion out there about Razor.
The comparison you see most often is between MVC and Razor. Microsoft currently recommends using Razor to reduce complexity (https://docs.microsoft.com/dotnet/architecture/porting-existing-aspnet-apps/comparing-razor-pages-aspnet-mvc
). Note that there is an MVC-like approach in Razor, except that Microsoft uses different terms for the various components because using different terms makes technology look new and shiny. So, the MVC action is now a handler, the MVC ViewModel is now a PageModel
, and the view is now a Razor Page. When working in MVC, these elements appear in three different locations and you need to modify them individually, which makes MVC error prone because people forget to do things unless they have a string tied around their finger. When working with Razor, the handler and PageModel
appear in a single file, while the view appears in a separate file, all under the Pages folder in the project. Here are some other differences between MVC and Razor:
.cshtml
file with a code-behind file.A Razor website uses a number of files that may seem a little daunting at first, but they make sense after you take them apart rather than attempt to view them as a whole. Here is the list of files for the website in the order in which they’re typically used to create the presentation:
Program.cs:
Contains the Main()
method and application startup code. This code includes the creation of a IHostBuilder
object, webBuilder
, which actually starts the website using the Startup
class found in Startup.cs
.Startup.cs:
Defines the website's underlying configuration. This is where the application adds Razor pages to the site and determines how those pages will interact. The “Developing a Basic Web App” section of Chapter 1 of this minibook shows you some of what goes on in the Startup
class, such as adding UseEndpoints()
calls and substituting calls to app.UseDefaultFiles();
and app.UseStaticFiles();
to support the use of static HTML files on a site. The calls made within this class determine the characteristics of the website interactions.
_ViewImports.cshtml
: Specifies the application imports, Razor page namespace, and helpers used to render the Razor pages (@addTagHelper
). You can read more about tag helpers at https://docs.microsoft.com/aspnet/core/mvc/views/tag-helpers/intro
._ViewStart.cshtml
: Declares which layout page to use for a website. The use of this file eliminates the need to declare the layout page in every content page for the website, reducing potential errors. You can use this feature to change the entire feel of a website with a single coding change._ValidationScriptsPartial.cshtml
: Contains pointers to the scripts used to validate the Razor page content. You see older websites that have the scripts actually included in this file. Newer versions of ASP.NET core pages include references to external files to use for validation purposes, which makes maintaining the setup easier._Layout.cshtml
: Determines the overall appearance of the Razor pages. Consequently, when you open this file, you see what appears to be an entire HTML page with special inclusions for specific page content, such as @ViewData["Title"]
, which determines the page title. This is also where you find common page elements, such as the navbar
used to move between pages. However, this file doesn't contain any actual content — it’s simply the layout used to contain the specific page layout.index.cshtml
: Defines the first content page for a website. In all cases, the content appears within a layout specified by the layout document, which is usually _Layout.cshtml
.index.cshtml.cs
: Contains the code-behind for the associated content page. The code-behind performs tasks such as handling user-interface events and performing some types of dynamic page setups. Razor pages are based on the PageModel
class (https://docs.microsoft.com/dotnet/api/microsoft.aspnetcore.mvc.razorpages.pagemodel
).You see other content pages provided with the project that include Privacy.cshtml
and Error.cshtml
. These additional content pages work the same as index.cshtml
, but they're called upon as needed. There is a link from the home page to Privacy.cshtml
. The Error.cshtml
file is called upon only when there is an error.
A Razor page is a combination of elements that include both HTML and code. How you combine the elements depends on what you plan to do. However, all your code will begin with an at sign (@
). For example, if you want to add a date to the default Index.cshtml page, you might use the following code shown in bold:
<div class="text-center">
<h1 class="display-4">Welcome</h1>
<p>It's @DateTime.Now.ToLongDateString()</p>
<p>Learn about <a href="https://docs.microsoft.com/aspnet/core">
building Web apps with ASP.NET Core
</a>.</p>
</div>
Notice that this paragraph uses a combination of an HTML tag, <p>
, plain text, and a call to @DateTime.Now.ToLongDateString()
. The date will change every time someone loads the page. After that, however, it remains static. A lot of content falls into this category, and you should use this technique whenever possible to make things simple. The output of this call comes in the following form:
It's Friday, October 15, 2021
There are some special rules for working with code on a Razor page, but the code appears as a form of C# and uses C# syntax rules. You could just as easily get the long date in your C# console application using DateTime.Now.ToLongDateString()
as you can when working with Razor. So, it’s important not to overthink how to do things.
Many sources online (such as https://docs.microsoft.com/aspnet/core/razor-pages/
) show lots of ways to write code in Razor, but few of them look at simple tasks, which can mean that they can be quite hard to comprehend. To avoid frustration, you need something easier. The following sections look at two basic tasks that you might perform on any web page.
The previous section showed a simple way to display the current date on your page. You actually have multiple ways to work with simple kinds of output depending on what you want to do. For example, you can add a field to your code to perform more complex kinds of output like this:
@{
var showString = "This is a string.";
}
You can then use showString
in your page like this:
<p id="StringOutput">@showString</p>
The creation of showString
can include any level of complexity needed to provide the data required. However, at its root, showString
is simply an output that provides online data.
You can also create custom classes to interact with your Razor page. For example, you might create a class like this one in Index.cshtml.cs
:
public class CodeBehind
{
public string aString = "This is a code-behind string.";
}
@{
var codeBehind = new CodeBehind();
}
You then use the instance variable, codeBehind
, on your page like this:
<p id="StringOutput2">@codeBehind.aString</p>
Sometimes you just need to make a simple change to a web page and don’t really want to create a complex design that will make your friends envious. Web pages are static, unlike applications on desktops that can be as dynamic as they want. Consequently, you need some means of requesting an update from the server or provide client-side code to make the change locally, but the fact is that you need some means of changing the web page content through a request. The easiest way to perform this task using Razor is to create code-behind. In this case, the code appears in Index.cshtml.cs
as part of the IndexModel
class like this:
public string changingString { get; set; }
public void OnGet()
{
changingString = "This is the changing string.";
}
public void OnPost()
{
changingString = "The string has changed.";
}
The focus of this code is the changingString
property, which is set by OnGet()
during the initial page request. Whenever your browser requests a new page, the code-behind looks at OnGet()
to see whether there's something special to do. Later, when you request a change, you can use the post method in your page code to call on OnPost()
, which assigns a new value to changingString
. The code in Index.cshtml
looks like this:
<p>@Model.changingString</p>
<form method="post">
<button>Click to Change Text</button>
</form>
Mind you, that <form>
really is around the individual <button>
, not around the page as a whole. Notice that you use @Model.changingString
to access changingString
. A good many online examples show @changingString
, which doesn't work at all (although it might have in the past). The <button>
is just a simple HTML button without any sort on onclick
handler added to it, so it doesn't look like it should work, but it actually does.
Of course, you might need to provide multiple buttons — imagine that! The simple post method doesn’t work in this case, but you have another option with a very small change. You give the OnPost()
method a more specific name, one that has a handler name to go with it. So, the following code has OnPostView1()
and OnPostView2()
:
public void OnPostView1()
{
changingString = "The string has changed.";
}
public void OnPostView2()
{
changingString = "Another Change!";
}
Now you need another method for accessing the methods. You get this method by simply adding the asp-page-handler
attribute to the <form>
tag, as shown here:
<form asp-page-handler="view1" method="post">
<button>Click to Change Text</button>
</form>
<form asp-page-handler="view2" method="post">
<button>Try Another Change</button>
</form>
Variables in Razor follow the same course as variables in C#. You have access to the same data types, the same operators, and so on. However, you may find yourself using them differently. In many cases, variables provide assistance with creating content. For example, you might want to know whether someone is using view1
from the examples earlier in the chapter. In this case, you can simply test the content of changingString
and place it in a bool
in the Index.cshtml
file, like this:
@{
bool isView1 = Model.changingString == "The string has changed.";
}
This sort of test helps you decide how to process information. You can use this sort of testing to change the content of the page after each reload. It helps you do things like decide whether the user is logged into the website or if there is a connection to the database you need. The web page content can change to meet specific demands without having to rely on code-behind to do it.
The pattern for working with logical statements in Razor is very much the same as working with logical statements in C#, but with a slight twist. As with all code in a .cshtml
page, you must precede the logical structure with an @
sign. The following sections discuss the basic logical statements and how to use them as part of a Razor page.
The “Creating Variables” section, earlier in this chapter, discusses how to create variables in Razor. You can use these variables in all sorts of ways, but one of the most common methods is to modify the page appearance to meet specific conditions. The following @if
statement uses the isView1
variable created earlier to change the appearance of the page:
@if(isView1)
{
<p>Using View1</p>
}
else
{
<p>Not Using View1</p>
}
Notice that this looks about the same as any other if
statement in C#. However, the output is different. In this case, you see that the selection of a particular logical path results in HTML output rather than the execution of code. Also note that the else
clause of the if
statement lacks the @
sign.
Razor supports switches, which can save you considerable time typing. Say you create an int
named ViewNumber
like this:
@{
int ViewNumber;
if (Model.changingString == "This is the changing string.")
ViewNumber = 0;
else if (Model.changingString == "The string has changed.")
ViewNumber = 1;
else
ViewNumber = 2;
}
The if
statement appears normal in this case because it appears within an @
block. The code places a value in ViewNumber
that corresponds to the current text in changingString
. You can use a switch
to process ViewNumber
, like this:
@switch(ViewNumber)
{
case 0:
<p>Using Original View</p>
break;
case 1:
<p>Using View 1</p>
break;
case 2:
<p>Using View 2</p>
break;
default:
<p>Unrecognized View</p>
break;
}
Except for the @
sign and the use of HTML tags, the switch
statement in this example could appear in any C# file. All the same rules apply as when working with C#, so you don't have to do anything weird except remember that @
sign and the fact that you can do something interesting with the HTML.
As you might expect, Razor implements loop processing using the same techniques that you use in C#. The difference is in how you process the data and the outcome of any tasks you perform. The following sections show how to create an array, so you have source data to process, and then how to process the data using a number of different techniques.
An array in Razor can take any form that you use in C#. Of course, you need to create it within an @
block or use an existing array of values. Here's an example of creating an array for use within the example:
@{
string[] Colors =
{ "Red", "Orange", "Yellow", "Green", "Blue", "Violet" };
}
As you can see, there is nothing weird here. The same techniques you use to create a C# array normally apply here as well.
The Colors
array created in the previous section provides a perfect way to test for
loop processing in Razor. The following code uses the Colors
array as a source and displays each of the colors in turn:
<h3>Colors</h3>
<ul class="text-left">
@for (var i = 0; i < Colors.Length; i++)
{
<li>Color @i is @Colors[i]</li>
}
</ul>
As with the previous examples, you need to use the @
sign before you create the for
loop. In addition, this example shows how you might process a source array so that it appears correctly onscreen. Remember that you can use all the same techniques as you normally use with a for
loop to process data in C#.
The essential difference between a for
loop and both foreach
and while
is that the former processes data a known number of times, while the latter process data an unknown number of times until they're finished. In many cases, the choice of looping mechanism comes down to style and preference. Some developers like one form and other developers like another form. Here is the same output provided in the previous example using a foreach
statement.
<h3>Colors Using Foreach</h3>
@{
int counter = 0;
}
<ul class="text-left">
@foreach (string item in Colors)
{
<li>Color @counter is @item</li>
counter++;
}
</ul>
The example begins by declaring an int
variable, counter
, to keep track of the color number. It then uses the @foreach
loop to process the Colors
array. The differences you should notice in this version of the example are the technique used to produce the output and the fact that the example freely mixes HTML and C# code.