Lesson 2 - First web application in ASP.NET Core MVC

C# .NET ASP.NET ASP.NET Core ASP.NET Core MVC Basics First web application in ASP.NET Core MVC

In the previous lesson, Introduction to the MVC architecture in ASP.NET Core, we introduced the MVC architecture. Today, we're gonna use this knowledge in practice and program our first web application in ASP.NET Core MVC.

The course assumes basic knowledge of C# .NET and HTML. In case you won't understand it, first read the basic courses for these technologies where everything is explained.

I write these tutorials for Visual Studio 2017, the interface of other versions may be slightly different, but not dramatically, for sure, so you should find it all there. For developing in ASP.NET Core 2 and higher, however, you need at least Visual Studio 2017 version 15.3.0 or higher and have the .NET Core 2.x SDK installed (or eventually, you can download it at https://www.microsoft.com/…ownload/core).

Creating a project

Our first web application will be a random number generator. Let's start by creating a new project in Visual Studio. Create a new project using the main menu as File -> New -> Project.

In the dialog, we select C# as the language, ASP.NET Core Web Application as the project type and we enter MVCRadomNumber as the name. If you don't have the "ASP.NET Core" category there, use the "Open Visual Studio Installer" link in the left part of the window. You can find ASP.NET Core in the Web & Cloud category, then you need to check .NET Core development tools for Web in the right part.

Once we confirm the dialog, the next one will appear with the template selection. The template is a predefined project structure which can be generated for us. We'll start with an empty project and therefore we'll choose Empty. Make sure you've selected ASP.NET Core version 2.0 or higher at the top. We confirm the form.

Empty ASP.NET Core project in Visual Studio

Directory structure

Although we've created an empty project, Visual Studio still generated several new files. This is because ASP.NET Core is a framework, a ready-made solution that we only adapt for our purpose.

The directory structure of an ASP.NET Core project in Visual Studio

In Solution Explorer on the right, we start by right-clicking on the project and choosing Add -> New folder to create the Models/, Views/ and Controllers/ folders. That's where we're gonna add the components of our application as we explained in the previous lesson.

Adding folders to an ASP.NET Core MVC project

Model

Let's start with the model. We'll add a simple class named Generator to the Models/ folder. It'll have the following contents:

public class Generator
{
    private Random random = new Random();

    public int GetNumber()
    {
        return random.Next(100);
    }
}

The class does nothing else than return a random number using a private instance of the Random class. Practically, such a model does make little sense, but it's the principle which is important for us, and in the future, we'll return e.g. an article from the database in the same way. So we've created the logical part of our application.

Controller

Now, we'll add Controller to the Controllers/ folder by right-clicking it and selecting Add -> Controller. We select MVC Controller - Empty as the type. Other types allow us e.g. to generate views which we'll use further in the course.

Creating a new controller in ASP.NET Core MVC

As the name of the controller, we'll enter HomeController. The controller name should always end with Controller. HomeController is the standard name of the controller which is called when we open the website (without specifying other parameters, so it displays the homepage).

Home Controller in ASP.NET Core MVC

Visual Studio has generated a new class for us that looks like this:

public class HomeController : Controller
{
    public IActionResult Index()
    {
        return View();
    }
}

The Index() method is called on the controller at the moment when the user requests the page the given controller handles. This method is the place where we create an instance of the model, get data from it, and pass these data to the view.

The method uses the IActionResult interface as the return type, which represents an object that we send back to the browser when the request is complete. In our case, we send a template (the View object) to the browser. We can also send a file, redirect request, or even JSON data. We could even return just a string that would be then printed in the browser.

Passing data to the view

We have 3 collections available for passing data, accessible both in the controller and later in the view. In the controller, we'll fill the collection with the data from the model and, in the view, render the data from this collection to the prepared HTML template.

We can use the following collections:

  • ViewData - A collection of the Dictionary kind, where we put individual variables for the template under string keys. It was used especially before C# introduced the dynamic keyword.
  • ViewBag - ViewBag uses dynamic properties that have been in C# .NET since the version 4.0. Instead of keys, we write directly into the properties which are created on ViewBag.
  • TempData - A quite confusing collection used to pass data, especially during redirection. Data is deleted when the request is complete.

It doesn't matter whether we'll pass the data to the template using ViewBag or ViewData, since ViewBag uses ViewData internally for its properties. This means that whatever you store into ViewBag is also accessible as a ViewData item and vice versa. However, the advantage of ViewBag is that we don't have to perform typecasting to get a concrete type. Microsoft mostly uses its older ViewData in its documentation, partly because ViewBag isn't currently available in Razor Pages (another page type that can be created using Core). The choice of the collection doesn't matter.

Sometimes (with the MVVM architecture - Model-View-ViewModel), you can also encounter passing data to the view through a special data object, a ViewModel, but we'll show you this way further in the course.

Let's modify the Index() method in the controller so that the method gets data from the model before the view is returned and saves it to ViewBag:

public IActionResult Index()
{
    Generator generator = new Generator();
    ViewBag.Number = generator.GetNumber();
    return View();
}

In order to access the model, we need to add using. Even though you all know it, just to be sure, I repeat that you click on the red underlined name of the Generator class and then click on the bulb on the left, and add the given using. If it doesn't work, you can add it manually as using MVCRandomNumber.Models to the very top of the file. We're done with the controller. We responded to the index page request and wired the model with the view.

View

In our application, we are now missing the template (view) in which we'll display the output for the user. I'll use both the "template" and "view" terms in the course, and I'll always mean view. We add the view easily in the relevant controller. Right-click anywhere in the Index() method and choose Add View.

Adding a view in the ASP.NET Core MVC application

We name the view as same as the method. We confirm.

Adding view in ASP.NET Core MVC

We've generated an HTML template with the following contents:

@{
    ViewData["Title"] = "Index";
}

<h2>Index</h2>

At the top, we can see the at-sign and C# code. That's the Razor engine syntax, which is used to embed C# code into HTML. There are several other rendering engines, but they aren't very popular.

We already know that all the logic should be in the models. In the views, we'll use C# just to print the final data we've obtained from the models. We should keep the smallest number of Razor directives as possible in our templates.

Based on the contents of the generated template, you've certainly found out that the template isn't the final HTML page, but only a part of it which will be inserted into the layout. Since we haven't defined any layout yet, the output won't be valid HTML. We'll keep it like this in this first example. The template will only contain one particular subpage of our website. We'll set the title to the template and insert the number from the model using ViewBag and the Razor @ directive. The template's code is now going to be as follows:

@{
    ViewBag.Title = "Online generator of random numbers";
}

<h2>Random number</h2>
<p style="font-size: 2em;">@ViewBag.Number</p>

I've replaced the original code setting the title using ViewData by ViewBag to make it the same but this change isn't necessary. We print the number from ViewBag where it was stored by the controller. It got it from the model that generated it.

Middleware, request handling, and routing

If we run our application now (Ctrl + F5), it'll print only the "Hello World" message and our controller won't be called at all. Since we've chosen an empty template at the beginning for the sake of clarity, we must first route the HomeController to be called as default. This mechanism wiring URLs to controllers or other parts of the application is called routing and we'll find it in Startup.cs.

The file contents look like this:

// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
}

// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

We'll put middleware into this method. You can imagine it in ASP.NET Core as a series of filters through which the user's request passes before finding the right one which processes it. They are extension methods on the IApplicationBuilder interface (some of you may recall the Chain of Responsibility design pattern).

Each middleware in the chain has only a limited and specific role in processing the request - the first one can only e.g. perform a logger function, another middleware can search for a cookie or authorization token, and if it doesn't find it, it returns an error message or redirects the user. For example, the UseFileServer() middleware allows us to return static contents as the response (scripts in JavaScript, images, CSS files, etc.) of our project and so on.

In order to route the user to HomeController immediately after opening our application, we can use the app.UseMvcWithDefaultRoute() middleware, which we put in the middle of the Configure() method.

These types of middleware, which route the request to controllers, are called routes.

public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
    if (env.IsDevelopment())
    {
        app.UseDeveloperExceptionPage();
    }

    app.UseMvcWithDefaultRoute(); // We added this line

    app.Run(async (context) =>
    {
        await context.Response.WriteAsync("Hello World!");
    });
}

Once the application starts, the HomeController is now created, and its public Index() method called. An equivalent to the default routing using UseMvcWithDefaultRoute() would be the following callback, which would explicitly set the controller and its method:

app.UseMvc(routeBuilder => routeBuilder.MapRoute(name: "Default",
    template: "{controller}/{action}/{id?}",
    defaults: new { controller = "Home", action = "Index" }
));

However, let's keep the previous variant, we'll come back to the routing later. Unfortunately, when we start the project now, we have an unpleasant error message waiting for us (or the exception in Visual Studio):

Application error – InvalidOperationException: Unable to find the required services

The ASP.NET Core Framework consists of a large number of "granular" services and components that are needed for the MVC to work properly. To work as we expect, we must first add these services to our application (which is also what the exception text tells us to).

Therefore, we move to the ConfigureServices() method and by calling AddMvc() we add everything needed for the MVC to work to the services collection of our application:

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
}

We run the project. You'll see the following, this time, correct result:

ASP.NET Core MVC web application – Random number

You'll have a different port than I have on the screenshot.

Summary

Let's finally sum up how the whole application works.

The MVC architecture in ASP.NET Core MVC

First, the user request is processed by our middlewares and routed to the HomeController. Then the Index() method is called. It asks the model for data and stores the data in ViewBag. Next, the view is rendered, which, using the Razor syntax, prints data from the Viewbag to the specific places in the template. The finished page is sent to the user.

The source code of today's project is downloadable below, as in all the lessons. If you have failed at anything, you can correct your mistake easily by using our project. In the next lesson, Form handling in ASP.NET Core MVC, we'll look at the forms handling.


 

 

Article has been written for you by David Capka
Avatar
Do you like this article?
No one has rated this quite yet, be the first one!
The author is a programmer, who likes web technologies and being the lead/chief article writer at ICT.social. He shares his knowledge with the community and is always looking to improve. He believes that anyone can do what they set their mind to.
Unicorn College The author learned IT at the Unicorn College - a prestigious college providing education on IT and economics.
Activities (2)

 

 

Comments

To maintain the quality of discussion, we only allow registered members to comment. Sign in. If you're new, Sign up, it's free.

No one has commented yet - be the first!