Models, Views, and Controllers

In this sample chapter from ASP.NET Core Application Development: Building an application in four sprints, the author team recaps the basics of models, views, controllers, middleware, and dependency injection.

It was a surprise when Adrian popped over to Danielle’s cubicle. Maybe more surprising was the furrowed brow he was sporting. “Got a minute?” he asked in a hushed tone, and then walked away without waiting for an answer. She nodded a confused yes, mostly to herself, and slowly got up to follow him to a side room. She scanned around the development pit, but didn’t see anyone watching, much less interested, and couldn’t really get a read on why he was assuming the role of secret agent in this software docu-drama.

He closed the door behind her as she stepped into the room. “Look, I’m a CSS guy. I’m not going to sugar coat it. I know my way around jQuery well enough to troubleshoot, but I’m no programmer.” He looked tense and Danielle wondered what he’d been thinking. “Everyone else seems to be buying into this shift over to Core or whatever it’s called...but I’m...I mean, come on, I run Linux on my laptop and I don’t even use Office.” The room quickly drew silent.

“Are you still nervous, Adrian? Marc had mentioned that you were worried about the cuts.” Danielle was nervous too, truth be told. She had lost some good friends herself when the merge went through, but she wasn’t sure that was what he needed to hear at the moment.

“Well yeah, I guess,” he replied. “But my knowledge of MVC is that it stands for Model-View-Controller, and I haven’t taken a look any deeper than that. And you guys keep referring to it as a framework. If it’s a framework, but you have to make your own models, views, and controllers, then the MVC part seems more than a little misleading, don’t you think?”

He had a point. “Well, yeah, that’s actually pretty true,” said Danielle.

“I’m coming from a different view here; I just want to wrap my head around it. I want to learn, but I’m not sure I know where to start, either.” Adrian pounded back the rest of his coffee like it was about to expire. “I know they said we’re safe if we’re still here, but I don’t want to get caught with my feet standing still if they think there’s still room to shrink the team.”

“Okay, look,” said Danielle, “I’ve got a bit of time right now, let’s run through the basics and we’ll learn together. We’re all going to be fine.”

The M, the V, and the C of it

Let’s face it, the MVC Framework is a pretty boring name. The acronym used in the title is from the well-known Model-View-Controller pattern, and it helps to organize a project. If you’re familiar with it, the name literally spells out some of the original intent to separate concerns, and moves away from the other common pattern at the time known as Page-Controller. The name can also be misleading. The framework is much more than just models, views and controllers. ASP.NET Core MVC has a growing set of tooling and libraries available to that help developers create great applications, worthy of the modern web world.

Let’s do a quick recap on the aspects you should already understand, and then move into some more interesting aspects of the framework, officially known as ASP.NET Core MVC.

Diving into Models

First up is the letter M, so we’ll start with Models. The model represents the data that we need to properly render an experience, or part of an experience, for a customer. Customers are navigating to a page in the application that is data-driven, and models are the data part. However, as far as intent goes, the model in question is actually what you’ll be using to support the rendering of the view, and not the entity or entities in question in which you persist to the database.

Let’s consider this example from Alpine Ski House’s possible database design that deals with user account summaries, as shown in Figure 3-1. When you want to indicate to the user that she has a current season pass, you don’t want to return the list of season passes to the view and iterate over the collection to see if one exists that has not yet expired.

FIGURE 3-1 A screen shot showing a sampling of tables that might be used to model purchasing season passes

Returning all of this information to the view would be more than is required. Listing 3-1 contains a view model that might more closely approximate the information you would want to display to the user. As you can see, this is a POCO that sports the properties you can use to satisfy the view requirements without the view needing to make any decisions about what to display or any implementation of business logic. The view doesn’t need to know what qualifies as a current season pass nor does it need to sift through any of the purchase details or iterate through child records in related tables to make sense of the data.

The differentiation between what you craft for models on the front end, versus what you store in the database, is important not just for separating the concerns of the view and the business logic that supports it, but also for helping to prevent certain types of security issues. On the “write” side of things, when a view uses a database entity, the application becomes more likely to fall victim to overbinding bugs or attacks. Overbinding occurs when fields that weren’t anticipated from an incoming request are present in form or querystring parameters. The model binder sees the properties, doesn’t know that you hadn’t intended for them to be there, and kindly fills in your data for you. As an example, consider the class representing some kind of a digital asset in Listing 3-2.

This type of model can be used to display a list of resources made available to a user, and doing so is quite harmless. But, if you use the same object to receive edits for the record, a malicious user can exploit the fact that AssetOwnerId is a property and use that to take ownership of the asset. In fact, this is how Egor Homakov gained administrative privileges for the Ruby on Rails (RoR) repository on GitHub in 20121 (The technique in RoR is exploited through improperly checked models that make use of the mass assignment feature, an analog to automatic model binding in ASP.NET Core MVC. Thankfully, Homakov’s intentions were pure and no harm was done. We have learned from those binding conventions and habits of yore though. Today, we have many ways to protect ourselves, which we’ll cover later in Chapter 13, “Identity, Security and Rights Management,” but likely the easiest way is to make sure we’re using models that are appropriate to the task at hand.

Most of the examples you find for view models will likely use an entity directly as the model type for the view; however, the approach does not facilitate other aspects of software development, such as testing, nor does it help with separating concerns in your controllers. Using the entity directly in a view means that you’ve achieved an undesirable level of coupling from the database all the way up to the view.

A model should be everything you need to render your page after you’ve taken care of business logic and often has a flattened view of a denormalized record from several tables in the database. For these reasons, and considering the intent of the object you’re building up when you create a “model,” you should likely think of it as the “view model” due to its close relationship and responsibility to the view.

Views

Here, the view in question happens to start with V and is indeed the view we’re talking about in our new favorite acronym. Views in ASP.NET Core MVC are the files used to interleave parts of the model with the HTML needed in order to present the user with the intended user interface. If you create a new project from the default application template you will find all the views in the Views folder, or you can search Solution Explorer with the term “.cshtml,” which is the extension used for Razor views.

Using the Razor view engine and syntax you’ve seen through the last few iterations of the MVC Framework, you can switch effortlessly between the syntax used to control flow or access our model or services, and the markup required to generate HTML.

In Listing 3-3 we have created an unordered list with values from the model’s IncludedFamilyMembers collection. Razor lets you use C# inline with the HTML and is pretty smart about how it interprets what you throw at it. A simple @ character is enough for the parser to know you’re flipping into C#, and since angle brackets can’t be used at the start of a valid C# statement, it can tell when you’ve switched back to HTML. We’ll be covering Razor in greater detail in Chapter 11, “Razor Views.”

LISTING 3-3 An example of mixing C# and HTML in Razor Syntax.

Partial Views

Toolbars, authentication cues, shopping carts, parts of dashboards, and other similar components of your application often find themselves appearing on multiple pages, or even on all pages. In the name of Don’t Repeat Yourself (DRY), you can create these components using a partial view, which can in turn be used repeatedly from any other page. You’ll also see partial views referred to more simply as “partials.” We’ll use those terms interchangeably throughout the book.

Partials are not typically rendered on their own, but are used in composition of other views in your project. The first place you see this in any MVC application is likely to be in the _Layout.cshtml, where the view relies on partials to render the login status. Other common uses include using a partial view to render items in toolbars, shopping cart summaries like those you see at the top of an ecommerce site, or side bars with relevant data for the current page.

Child actions had to be rendered synchronously in previous versions of the MVC Framework, but the same ideas that made partials possible can now be used to construct view components and invoked asynchronously. We’ll talk about View Components more in Chapter 18, “Reusable Components,” which is important in certain scenarios to keep performance in check on the site. Complex generated views and partials that interact with services are examples of this, which we’ll talk about later in this chapter.

Before users can get the output of a view, and in order for you to load any kind of model into the view engine, we must talk a little bit about Controllers in your project.

Controllers (...and Actions!)

Controllers are the traffic cops of MVC applications, ensuring the right types of bits travel to and from the correct places. Controllers typically inherit from the base Controller class, but if you don’t need the functionality of the base class, you can also use the convention of ending your class name with “Controller,” such as in SeasonPassController.

The default convention assumes that you are putting your controllers in a folder called “Controllers” in the root of the project. This is no longer required because Core MVC actually does an assembly scan using the naming and inheritance conventions, but it’s still a recommended practice to organize your controllers in a recognized way. This helps other developers, including the future version of yourself, to easily manage and maintain the code base down the road.

As software developers, we use controllers as a container for related sets of handlers for incoming requests. These handlers are called actions and are implemented as methods in our controller class. Each method, or action, can accept zero or more parameters that are automatically filled in by the model binding step in the execution pipeline if they are presented by the incoming request.

As the authors of these “traffic cops,” our goal is to code our controllers using some well-accepted practices. The primary responsibility of an action is to process a request, validating the incoming parameters and creating an appropriate response.

From time to time, this also requires creating or requesting an instance of a model class, or producing an appropriate HTTP status code based response. You should try to avoid having any business logic in your controller, which is the responsibility of your model or other components, as well as keeping data access or external calls out of your actions, which should be part of your application services. This is represented in a high level in Figure 3-2.

FIGURE 3-2 An illustration showing how controllers are responsible for invoking business logic that helps to generate an appropriate HTTP response

Keeping these services external might seem to make things more complex, or raise questions like, “Who will create these services for me?” This is a great question and one that we’ll answer in the “Dependency Injection” section later in this chapter.