Mastering ASP.NET Core 2.0

3.8 (10 reviews total)
By Ricardo Peres
  • Instant online access to over 7,500+ books and videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Getting Started with ASP.NET Core

About this book

ASP.NET is an open source web framework that builds modern web apps and services. This book is your one-stop guide to the new features of ASP.NET Core 2.0, including web APIs and MVC. We begin with a brief overview of the basics, taking you through the MVC pattern, platforms, dependencies, and frameworks. We then move on to setting up and configuring the MVC environment before talking about routing and advanced routing options. Next, we'll look at model binding, controllers and actions, filters, user authentication, and testing.

Moving on, you’ll learn about all the aspects of syntax and processes when working with Razor. You’ll be introduced to client-side development and will get to know about the security aspects of ASP.NET Core. We will also look at Microservices with ASP.NET Core. Finally, you’ll find out how to deploy ASP.NET Core to new environments such as Azure, AWS, and Docker. By the end of the book, you will be well versed with development in ASP.NET Core and will have a deep understanding of how to interact with the framework and work cross-platform.

Publication date:
November 2017
Publisher
Packt
Pages
494
ISBN
9781787283688

 

Chapter 1. Getting Started with ASP.NET Core

Welcome to my new book on ASP.NET Core!

.NET and ASP.NET Core are relatively new in the technological landscape, as they have been officially released only last August. Having the .NET part in the name, it would seem that these would probably only be new versions of the highly popular .NET Framework, but that is not the case, we are talking about something that is truly new!

It's not just multi-platform support (howdy, Linux!), but it's so much more. It's the new modularity in everything, the transparent way by which we can now change things, the source code just in front of our eyes teasing us to contribute to it, to make it better, is indeed a lot different!

In this first chapter, we are going to talk a bit about what changed in ASP.NET and .NET in the Core versions, and also about the new underlying concepts, such as OWIN, runtime environments and dependency injection. Then I'll present in brief the sample project that will accompany us through the book.

In this chapter, we will cover the following topics:

  • History of ASP.NET Core
  • Introduction to .NET Core
  • Inversion of control and dependency injection
  • OWIN
  • The MVC pattern
  • Hosting
  • Environments
  • The sample project
 

Introducing ASP.NET Core


Microsoft ASP.NET was released 15 years ago, in 2002, as part of the then shiny new .NET Framework. It inherited the name ASP (Active Server Pages) from its predecessor, with whom it barely shared anything else, other than being a technology for developing dynamic server-side contents for the internet, which ran on Windows platforms only.

ASP.NET gained tremendous popularity, one has to say, and competed hand-to-hand with other popular web frameworks such as Java Enterprise Edition (JEE) and PHP. In fact, it still does, with sites such as BuiltWith giving it a share of 21% (ASP.NET and ASP.NET MVC combined), way ahead of Java (https://trends.builtwith.com/framework). ASP.NET was not just for writing dynamic web pages. It could also be used for XML (SOAP) web services, which, in early 2000, were quite popular. It benefited from the .NET Framework and its big library of classes and reusable components, which made enterprise development almost seem easy!

Its first version, ASP.NET 1, introduced Web Forms, an attempt to bring to the web the event and component model of desktop-style applications, shielding users from some of the less friendly aspects of HTML, HTTP, and state maintenance. To a degree, it was highly successful, one could easily, using Visual Studio, create a data-driven dynamic site in just a few minutes! A great deal of stuff could be accomplished merely through markup, with no code changes (read, compile) needed.

Version 2 came along a few years afterwards, and among all the other goodies, brought with it extensibility in the form of a provider model. A lot of its functionality could be adapted by the means of custom providers. Later on it received the addition of the AJAX Extensions which made AJAX-style effects astonishingly easy. It set the standard for years to come, leaving only room for more components.

Precisely, following versions 3.5, 4, and 4.5 only offered more of the same, with new specialized controls for displaying data and charts for retrieving and manipulating data and a few security improvements. A big change was that some of the framework libraries were released as open source.

Between versions 3.5 and 4, Microsoft released a totally new framework, based on the Model-View-Controller (MVC) pattern, and mostly open source. Although it sits on top of the infrastructure laid out by ASP.NET, it offered a whole new development paradigm, which this time fully embraced HTTP and HTML. It seemed to be the current trend for web development across technologies, and the likes of PHP, Ruby and Java, and .NET developers were generally pleased with it. ASP.NET developers had now two choices, Web Forms and MVC, both sharing the ASP.NET pipeline and .NET libraries but offering two radically different approaches to getting contents to the browser.

In the meantime, the now venerable .NET Framework had grown adult in an ever changing world. In the modern enterprise, the needs have changed, and sentences such as runs on Windows only or we need to wait XX years for the next version became hardly acceptable. Acknowledging this, Microsoft started working on something new, something different that would set the agenda for years to come ... enter .NET Core!

In late 2014, Microsoft announced .NET Core. It was meant to be a platform-independent, language-agnostic, free and open source full rewrite of the .NET Framework. It's main characteristics were as follows:

  • The base class libraries of .NET were to be rewritten from scratch, while keeping the same (simplified) public APIs, which meant not all of them would be available initially
  • Being able to also run on non-Windows operating systems, specifically, several Linux and macOS flavors, and in mobile devices, so all Windows-specific code (and APIs) would be discarded
  • All of its components were to be delivered as NuGet packages, meaning that only a small bootstrap binary would need to be installed in the host machine
  • There was no longer a dependency (or, let's say, a very close relationship) with IIS, it should be able to run auto-hosted or inside a hosting process, like, well, IIS
  • It would be open source, and developers would be able to influence it, either by creating tickets or by submitting change requests

This eventually took place in July 2016, when version 1.0 of .NET Core was released. The .NET developers could now write once and deploy (almost) everywhere and they finally had a say on the direction the framework was taking!

Rewriting from scratch the whole .NET Framework is a task of epic proportions, so Microsoft had to make decisions and define priorities. One of them was to ditch ASP.NET Web Forms and to only include MVC. So gone were the days when ASP.NET and Web Forms were synonyms, and the same happened with ASP.NET Core and MVC, it's now just ASP.NET Core! And it's not just that the ASP.NET Web API, which used to be a different thing, is now merged with ASP.NET Core as well, a wise decision from Microsoft, as basically the two technologies, MVC and Web API, had a lot of overlap and even had classes with the same name for pretty much the same purpose.

So, what does this mean for developers? Here are my personal thoughts:

  • C# only, no Visual Basic.NET for the time being; not a bad thing, in my opinion.
  • Open source is great! If you want to change anything, just grab the code from GitHub and make the changes yourself! If they're good enough, chances are, others may be interested in them too, so why not submit a pull request to have them integrated?
  • We don't need to decide upfront if we want to use MVC or the Web API, it's just a matter of adding one or two NuGet packages anytime and adding a couple of lines to the Startup.cs file; the same controller can serve both API and web requests seamlessly.
  • Attribute routing is built-in, so there's no need for any explicit configuration.
  • ASP.NET Core now uses Open Web Interface for .NET (OWIN) based middleware and configuration, so you will need to change (significantly) your modules and handlers so that they fit into this model; MVC/Web API filters are basically the same.
  • No dependency on IIS or Windows, meaning, we can easily write our apps in good old Windows/Visual Studio and then just deploy them to Azure/AWS/Docker/Linux/macOS. It's actually pretty cool to debug our app in Docker/Linux from Visual Studio! It can run self-hosted in a console application too.
  • A consequence of the latter, no more IIS Manager or web.config/machine.config files.
  • Not all libraries are already available for .NET Core, meaning, you will either need to find replacements or implement the features yourself. The site https://icanhasdot.net/Stats has a good list of whatever is/is not available for .NET Core and there is also a list in the project's roadmap at https://github.com/dotnet/core/blob/master/roadmap.md.
  • Even the core (pun intended) .NET Core classes are still lacking some methods that used to be there, take, for example the System.Environment class.
  • You need to handpick the NuGet packages for the libraries you want to use, including for classes that you took for granted in the old days. For .NET; this includes for example, System.Collections (https://www.nuget.org/packages/System.Collections), as they are not automatically referenced. Sometimes it's hard to find out which NuGet package contains the classes you want, when this happens http://packagesearch.azurewebsites.net may come in handy;
  • No more Web Forms (and the visual designer in Visual Studio), now it's MVC all the way!
 

.NET Core


Talking about ASP.NET Core without explaining .NET Core is somewhat cumbersome. .NET Core is the framework everyone is talking about, and for good reasons. ASP.NET Core is probably its most interesting API now, as it no longer (for the time being, that is) includes any other productivity/graphical user interface library. You heard it right, no more Windows Forms or Windows Presentation Framework or even Windows Services!

And why is that? Well, all these APIs relied heavily on Windows native features; in fact, Windows Forms was merely a wrapper around the Win32 API that has accompanied Windows since its early days. Because .NET Core is multi-platform, it would be a tremendous effort to have versions of these APIs for all supported platforms. By all means this doesn't mean, of course, that it won't happen; it's just that it hasn't happened yet.

With .NET Core, a host machine only needs a relatively small bootstrap code to run an application; the app itself needs to include all the reference libraries it needs to operate. Interestingly, it is possible to compile a .NET Core application to native format, thus producing a machine-specific executable that includes in it all the dependencies, and can even be run in a machine without the .NET Core bootstrapper.

As I said previously, .NET Core was written from scratch, which unfortunately means that not all the APIs that we were used to have been ported. Specifically, as of .NET Core 1.1 and 2.0, the following features are still missing:

  • ASP.NET Web Forms (System.Web.UI)
  • XML Web Services (System.Web.Services)
  • LINQ to SQL (System.Data.Linq)
  • Windows Forms (System.Windows.Forms)
  • Windows Presentation Foundation (System.Windows and System.Xaml)
  • Windows Communication Foundation server-side classes (System.ServiceModel)
  • Windows Workflow Foundation (System.Workflow and System.Activities)
  • .NET Remoting (System.Runtime.Remoting)
  • Some ADO.NET APIs, such as DataSet/DataView (System.Data), and parts of the ADO.NET provider model (System.Data.Common)
  • Code generation (System.CodeDom)
  • Distributed transactions (System.Transactions)
  • Active Directory/LDAP (System.DirectoryServices)
  • Enterprise Services (System.EnterpriseServices)
  • Email (System.Net.Mail)
  • XML and XSD (System.Xml.Xsl and System.Xml.Schema)
  • IO ports (System.IO.Ports)
  • Managed Addin Framework (System.Addin)
  • Speech (System.Speech)
  • Configuration (System.Configuration), this one was replaced by a new configuration API
  • Windows Management Instrumentation (System.Management)
  • Drawing functionality (System.Drawing), although some structures exist
  • Application Domains and sandboxing (System.AppDomain)
  • Windows Registry (Microsoft.Win32)

This is by no means an exhaustive list. As you can see, there are a lot of features missing. Still, it is quite possible to achieve pretty much whatever we need to, provided we do things in a different way and handle the extra burden!

The following APIs are new or still around and are safe to use:

  • MVC and Web API (Microsoft.AspNetCore.Mvc)
  • Entity Framework Core (Microsoft.EntityFrameworkCore)
  • Roslyn for code generation and analysis (Microsoft.CodeAnalysis)
  • All Azure APIs
  • Managed Extensibility Framework (System.Composition)
  • Text encoding/decoding and regular expressions processing (System.Text)
  • JSON serialization (System.Runtime.Serialization.Json)
  • Low-level code generation (System.Reflection.Emit)
  • Most of ADO.NET (System.Data, System.Data.Common, System.Data.SqlClient, System.Data.SqlTypes)
  • LINQ and Parallel LINQ (System.Linq)
  • Collections, including concurrent (System.Collections, System.Collections.Generic, System.Collections.ObjectModel, System.Collections.Specialized, System.Collections.Concurrent)
  • Threading, inter-process communication and task primitives (System.Threading)
  • Input/Output, compression, isolated storage, memory-mapped files, pipes (System.IO)
  • XML (System.Xml)
  • Windows Communication Foundation client-side classes (System.ServiceModel)
  • Cryptography (System.Security.Cryptography)
  • Platform Invoke and COM Interop (System.Runtime.InteropServices)
  • Universal Windows Platform (Windows)
  • Event Tracing for Windows (System.Diagnostics.Tracing)
  • Data Annotations (System.ComponentModel.DataAnnotations)
  • Networking, including HTTP (System.Net)
  • Reflection (System.Reflection)
  • Maths and numerics (System.Numerics)
  • Reactive Extensions (System.Reactive)
  • Globalization and localization (System.Globalization, System.Resources)
  • Caching (including in-memory and Redis) (Microsoft.Extensions.Caching)
  • Logging (Microsoft.Extensions.Logging)

Again, not the full list, but you get the picture. These are just Microsoft APIs made available for .NET Core; there are obviously thousands of others from different vendors.

Note

And why are these APIs supported? Well, because they are specified in a .NET Standard, and .NET Core implements this standard! More on this in a moment.

In .NET Core, there is no more a Global Assembly Cache (GAC), but there is a centralized location (per user) for storing NuGet packages, %HOMEPATH%.nugetpackages, which prevents you from having duplicate packages locally for all of your projects. .NET Core 2.0 introduced the runtime store, which is somewhat similar to GAC. Essentially, it is a folder on a local machine where some packages are made available and compiled for the machine's architecture. Packages stored there are never downloaded from NuGet; they are instead referenced locally and do not need to be included with your app. A welcome addition, I may add! Read more about metapackages and the runtime store here: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/metapackage.

The Microsoft.AspNetCore.All metapackage (a set of packages) includes:

  • All supported packages by the ASP.NET Core team
  • All supported packages by the Entity Framework Core
  • Internal and third-party dependencies used by ASP.NET Core and Entity Framework Core

Visual Studio templates for .NET Core 2 already reference this metapackage.

NuGet packages are at the heart of .NET Core, and mostly everything needs to be obtained from NuGet. Even projects in the same Visual Studio solution are referenced from one another as NuGet packages. When using .NET Core, you will need to explicitly add the NuGet packages that contain the functionality that you wish to use. It is likely that you may come across some of the following packages in some of your projects:

Package

Purpose

Microsoft.EntityFrameworkCore

Entity Framework Core

Microsoft.Extensions.Caching.Memory

In-memory caching

Microsoft.Extensions.Caching.Redis

Redis caching

Microsoft.Extensions.Configuration

General configuration classes

Microsoft.Extensions.Configuration.EnvironmentVariables

Configuration from environment variables

Microsoft.Extensions.Configuration.Json

Configuration from JSON files

Microsoft.Extensions.Configuration.UserSecrets

Configuration in user secrets (https://docs.microsoft.com/en-us/aspnet/core/security/app-secrets)

Microsoft.Extensions.Configuration.Xml

Configuration in XML

Microsoft.Extensions.DependencyInjection

Built-in dependency injection framework

Microsoft.Extensions.Logging

Logging base classes

Microsoft.Extensions.Logging.Console

Logging to the console

Microsoft.Extensions.Logging.Debug

Logging to debug

System.Collections

Collections

System.ComponentModel

Classes and interfaces used in the definition of components and data sources

System.ComponentModel.Annotations

Data annotations for validation and metadata

System.Data.Common

ADO.NET

System.Globalization

Globalization and localization APIs

System.IO

Input/Output APIs

System.Linq.Parallel

Parallel LINQ

System.Net

Networking APIs

System.Reflection

Reflection

System.Security.Claims

Security based upon claims

System.Threading.Tasks

Tasks implementation

System.Xml.XDocument

XML APIs

 

Again, not an exhaustive list, but you get the picture. You may not see references to all these packages, because adding one package that has dependencies will bring all these dependencies along, and big packages have a lot of dependencies.

There are no more .exe files; now all assemblies are .dll, which means they need to be run using the dotnet command line utility. All .NET Core applications start with a static Main method, as the old Console and Windows Forms did, but now we need the dotnet utility to run them. dotnet is a very versatile tool, and can be used to build, run, deploy, and restore NuGet packages, execute unit tests, and create NuGet packages from a project. As I said, it is also possible to compile an assembly to the native format, but we won't be covering it here.

.NET Core ships with built-in dependency injection (DI), logging, and a flexible configuration framework, which allows you to plug in your own providers if you so wish to. All of the new APIs (such as Entity Framework Core and ASP.NET Core) use these services uniformly. For the very first time, we see a coherent behavior across APIs.

Also, most productivity APIs like ASP.NET and Entity Framework allow replacing the services they're built upon by customized versions, allowing you to have them work the exact way you want them, provided, of course, you know what you are doing, and these services are generally based upon interfaces. Everything is much more modular and transparent.

Unit testing got first-class citizenship in .NET Core. Most new APIs were designed with testability in mind (think for example of the new in-memory provider for Entity Framework Core), and the tooling (dotnet) has an explicit option for executing unit tests, which can be written in any framework (presently, xUnit, NUnit, MbUnit and Microsoft, among others, have released unit test frameworks compatible with .NET Core). We will cover unit testing in this book in more detail.

 

Platforms


.NET Core works in the following platforms:

  • Windows 7 SP1 or higher
  • Windows Server 2008 R2 SP1 or higher
  • Red Hat Enterprise Linux 7.2 or higher
  • Fedora 23 or higher
  • Debian 8.2 or higher
  • Ubuntu 14.04 LTS/16.04 LTS, or higher
  • Linux Mint 17 or higher
  • openSUSE 13.2 or higher
  • Centos 7.1 or higher
  • Oracle Linux 7.1 or higher
  • macOS X 10.11 or higher

This covers all modern Windows, Linux and macOS distributions (Windows 7 SP1 was released in 2010). It may well work in other distributions, but these are the ones that have been thoroughly tested by Microsoft.

So, how does this work? It turns out that whenever you request a NuGet package that needs native libraries, not included in the operating system, these are also included in the .nupkg archive. .NET Core uses Platform Invoke (P/Invoke) to call the operating system-specific libraries. This means that you do not have to worry about it, the process to locate, add a NuGet package, and publish the project is the same no matter what the target operating system will be.

Keep in mind that platform independence is transparent to you, the developer, unless of course you also happen to be a library author, in which case you may need to care about it.

 

Dependencies and frameworks


Inside a .NET Core project, you specify the frameworks that you wish to target. What are these frameworks? Well, .NET Core itself, but the classic .NET Framework as well, Xamarin, Universal Windows Platform (UWP), Portable Class Libraries (PCL), Mono, Windows Phone, and more.

In the early days of .NET Core, you would either target .NET Core itself or/as well as, one of these other frameworks. Now it is advisable to target standards instead. According to Immo Landwerth of Microsoft (https://blogs.msdn.microsoft.com/dotnet/2016/09/26/introducing-net-standard):

.NET Standard solves the code sharing problem for .NET developers across all platforms by bringing all the APIs that you expect and love across the environments that you need: desktop applications, mobile apps & games, and cloud services:

  • .NET Standard is a set of APIs that all .NET platforms have to implement. This unifies the .NET platforms and prevents future fragmentation.
  • .NET Standard 2.0 is implemented by .NET Framework, .NET Core, Mono, and Xamarin. For .NET Core, this adds many of the existing APIs that have been requested.
  • .NET Standard 2.0 includes a compatibility shim for .NET Framework binaries, significantly increasing the set of libraries that you can reference from your .NET Standard libraries.
  • .NET Standard will replace Portable Class Libraries (PCLs) as the tooling story for building multi-platform .NET libraries.

Put in a different way:

  • .NET Standard is a specification that covers which APIs a .NET platform has to implement
  • .NET Core is a concrete .NET platform and implements the .NET Standard
  • The latest .NET Standard will always cover the highest .NET full framework released

David Fowler (https://twitter.com/davidfowl) of Microsoft came up with the following analogy:

interface INetStandard10
{
  void Primitives();
  void Reflection();
  void Tasks();
  void Collections();
  void Linq();
}
interface INetStandard11 : INetStandard10
{
  void ConcurrentCollections();
  void InteropServices();
}
interface INetFramework45 : INetStandard11
{
  // Platform specific APIs
  void AppDomain();
  void Xml();
  void Drawing();
  void SystemWeb();
  void WPF();
  void WindowsForms();
  void WCF();
}

It should make it very easy to understand. As you can see, all .NET APIs that need Windows (WPF, Windows Forms, Drawing) are only available in a specific platform (.NET 4.5), not a standard. Standards are for cross-platform functionality.

So instead of targeting a specific version, such as .NET 4.5.1, .NET Core 1.0, Mono, Universal Windows Platform 10, or Windows Phone 8.1, you target a .NET standard. Your project is guaranteed to work on all platforms that support that standard (or a higher one), either existing or waiting to be created. You should try to keep your dependency to the lowest standard possible, to increase the number of platforms where your app will work, if that is important to you.

The following table lists all the .NET Standards and the platforms they support:

 

.NET Standard

1.0

1.1

1.2

1.3

1.4

1.5

1.6

2.0

.NET Core

1.0

1.0

1.0

1.0

1.0

1.0

1.0

2.0

.NET Framework

4.5

4.5

4.5.1

4.6

4.6.1

4.6.1 4.6.2

4.6.1 vNext

4.6.1

Mono

4.6

4.6

4.6

4.6

4.6

4.6

4.6

5.4

Xamarin.iOS

10.0

10.0

10.0

10.0

10.0

10.0

10.0

10.14

Xamarin.Mac

3.0

3.0

3.0

3.0

3.0

3.0

3.0

3.8

Xamarin.Android

7.0

7.0

7.0

7.0

7.0

7.0

7.0

7.5

Universal Windows Platform

10.0

10.0

10.0

10.0

10.0

vNext

vNext

vNext

Windows

8.0

8.0

8.1

Windows Phone

8.1

8.1

8.1

Windows Phone Silverlight

8.0

 

This table represents the current mapping between the different .NET frameworks and the .NET Standard they implement at the time this book was written, and the latest version is always available at: https://github.com/dotnet/standard/blob/master/docs/versions.md.

Each .NET Standard version features some APIs and each higher version supports more, as you can see in the following table:

Version

#APIs

Growth %

1.0

7,949

1.1

10,239

+29%

1.2

10,285

+0%

1.3

13,122

+28%

1.4

13,140

+0%

1.5

13,355

+2%

1.6

13,501

+1%

2.0

32,638

+142%

.NET Core 2.0 and .NET Standard 2.0 were made available in August 2017 and now four frameworks target .NET Standard 2.0:

  • .NET Framework full
  • .NET Core 2.0
  • Xamarin
  • Mono

You can have your dependencies specified per target or for all targets. In the former case, all the dependencies need to support all targets and in the latter, we can have different dependencies for each target. You'll probably want a mix of the two, with common libraries as global dependencies and more specialized libraries specified only where available. If you target more than one standard (or framework), pay attention, because you may have to resort to conditional defines (#if) to target those features that only exist in one of them.

The .NET Standard FAQ is available in GitHub: https://github.com/dotnet/standard/blob/master/docs/faq.md.

Targeting .NET Core or the full .NET framework

It is important that you know that you can target the full .NET framework in an ASP.NET Core application! However, if you do it, you lose the platform independency, that is, you can only run it on Windows.

By default, an ASP.NET Core project targets netcoreapp1.0 or netcoreapp2.0, depending on whether you are targeting ASP.NET Core 1.x or 2.x, but you can change it in the .csproj file. If you just want to target one framework, modify the TargetFramework element like this:

<TargetFramework>net461</TargetFramework>

Or, if you want to target more than one, replace TargetFramework for TargetFrameworks :

<TargetFrameworks>netcoreapp2.0;net461</TargetFrameworks>

For more info, please refer to the Microsoft documentation: https://docs.microsoft.com/en-us/dotnet/core/tools/csproj.

 

Understanding the MVC pattern


Going back to ASP.NET now. So, for those of you that were still working with Web Forms, what is this MVC thing anyway, and where did it come from?

Let's face it, it was pretty easy in Web Forms to do terrible things, such as adding lots of/sensitive code in the page (which wouldn't be compiled until the page was accessed by the browser), adding complex business logic to a page class, having several megabytes of code in View State going back and forth on every request, and so on. There was no mechanism at all, other than the developer's discretion, to do things the right way. Plus, it was terrible to unit test it, because it relied on browser submission (POST) and JavaScript to have things working properly, such as binding actions to event handlers and submitted values to controls. There had to be a different solution, and in fact there was.

The Model-View-Controller (MVC) design pattern was defined in the late 1970s and early 1980s of the past century (scary, isn't it?). It was conceived as a way to properly segregate things that shouldn't conceptually be together, such as the code to render a user interface (UI) and the code that contains the business logic and data access that will feed and control that UI. In the MVC paradigm (and its offspring), we have controllers which expose public actions. Inside each action, the controller applies any business logic it needs to and then decides which view it should render, passing it enough information (the model) so that it can do its job. A controller knows nothing about UI elements, it just takes the data and execution context it needs to operate inside the action and goes from there. Likewise, a view will not know anything about databases, web services, connection strings, SQL, and the like, it just renders data, possibly taking simple decisions about the way to do it. As for the model, it's basically anything you want that contains the information required by the view, including lists of records, static user information, and more. This strict separation makes things much easier to manage, test and implement. By all means, the MVC pattern is not specific to the web, it can be used whenever this separation of concerns is useful, such as when we have a user interface and some code to control it.

The following diagram presents the relationship between views, controllers and models:

MVC is normally associated with Object-Oriented Programming (OOP), but there are implementations in a myriad of languages, including JavaScript and PHP. The .NET MVC implementation has the following basic characteristics:

  • Controller classes are either Plain Old CLR Object (POCO) or inherit from a base class, Controller. Inheriting from Controller is not required (unlike in previous versions), but it does make things slightly easier. Controller classes are instantiated by the ASP.NET Core dependency injection (DI) framework, which means they can have the services they depend upon passed into them.
  • Actions are public methods in a controller; they can take parameters, of both simple types as well as complex ones (POCOs); MVC uses what is called model binding to translate information sent from the browser (the query string, headers, cookies, forms, dependency injection, and other locations) into method parameters. The choice of which method from which controller to invoke from the request URLs and submitted parameters is achieved by a mix of a routing table, conventions, and helper attributes.
  • The model is sent from the controller to the view in the return of an action method and it can be basically anything (or nothing); of course, action methods for API calls do not return views, but can return a model together with an HTTP status code. Other ways exist to pass data to the view, namely, the view bag, which is essentially an untyped dictionary of data; the difference between the two is that the model is normally typed. A model is automatically validated and bound to the action method parameters.
  • Views consist of Domain-Specific Languages (DSL) files that are interpreted by a view engine and turned into something that the browser can interpret, such as HTML. ASP.NET Core features an extensible view engine framework but includes a single implementation, Razor. Razor offers a simple syntax that allows developers to mix HTML and C# to get hold of the model passed in and make decisions on what to do with it. Views can be constrained by layouts (Web Forms developers can think of layouts as master pages) and they can include other partial views (similar to web user controls in Web Forms). A view for the Razor view engine has the .cshtml extension, and cannot be accessed directly, only as the result of an action invocation. Views can be pre-compiled, so that syntax errors are detected sooner.
  • Filters are used to intercept, modify, or fully replace the request; built-in filters include preventing access to unauthenticated users or redirecting to an error page in the event of an exception occurring.

Now, there are other patterns similar in purpose to MVC, such as Model-View-Presenter (MVP) or Model-View-ViewModel (MVVM). We will only focus on Microsoft's implementation of MVC and its specifics. In particular, the version of MVC that ships with ASP.NET Core is version 6, because it builds on version 5 which was previously available for the .NET full framework, but both add and drop a couple of features. Because it now sits on the new .NET Core framework, it is fully based on OWIN, so there's no more Global.asax.cs file. More on this later on.

MVC, the way it is implemented in ASP.NET, focuses on:

  • URLs: They are now more meaningful and Search Engine Optimization (SEO) friendly
  • HTTP verbs: The verb now exactly states what the operation is supposed to do, like, GET for idempotent operations, POST for new contents, PUT for full content updates, PATCH for partial content updates, DELETE for removals, and more
  • HTTP status codes: For returning operation result codes, which is more important in the case of Web APIs

For example, issuing a GET request to http://somehost/Product/120 is likely to return a view for a product with id 120 and a DELETE request for the same URL will probably delete this product and return either some HTTP status code or a nice view informing us of the fact. URLs and their binding to controllers and actions are configurable through routes, and it is likely that this URL will be handled by some controller called ProductController and some action method that is configured to handle GET or DELETE requests. Views cannot be extracted from the URL because they are determined inside the action method.

We will cover Microsoft's implementation of MVC in depth in the following chapters. Being a .NET Core thing, obviously all of its components are available as NuGet packages. Some of those you will likely find are:

Package

Purpose

Microsoft.AspNetCore.Antiforgery

Anti-forgery APIs

Microsoft.AspNetCore.Authentication

Authentication base classes

Microsoft.AspNetCore.Authentication.Cookies

Authentication through cookies

Microsoft.AspNetCore.Authorization

Authorization APIs

Microsoft.AspNetCore.Diagnostics

Diagnostics APIs

Microsoft.AspNetCore.Hosting

Hosting base classes

Microsoft.AspNetCore.Identity

Identity authentication

Microsoft.AspNetCore.Identity.EntityFrameworkCore

Identity with Entity Framework Core as the store

Microsoft.AspNetCore.Localization.Routing

Localization through routing

Microsoft.AspNetCore.Mvc

The core MVC features

Microsoft.AspNetCore.Mvc.Cors

Support for Cross Origin Request Scripting (CORS)

Microsoft.AspNetCore.Mvc.DataAnnotations

Validation through data annotations

Microsoft.AspNetCore.Mvc.Localization

Localization based APIs

Microsoft.AspNetCore.Mvc.TagHelpers

Tag helpers functionality

Microsoft.AspNetCore.Mvc.Versioning

Web API versioning

Microsoft.AspNetCore.ResponseCaching

Response caching

Microsoft.AspNetCore.Routing

Routing

Microsoft.AspNetCore.Server.IISIntegration

IIS integration

Microsoft.AspNetCore.Server.Kestrel

Kestrel server

Microsoft.AspNetCore.Server.WebListener

(Microsoft.AspNetCore.Server.HttpSys in ASP.NET Core 2)

WebListener server (now called HTTP.sys).

See https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/httpsys.

Microsoft.AspNetCore.Session

Session functionality

Microsoft.AspNetCore.StaticFiles

Ability to serve static files

 

You may or may not need all these packages, but you should make yourself familiar with them.

Note

In ASP.NET Core 2.0, there is the Microsoft.AspNetCore.All NuGet metapackage. This includes most of the individual packages you will need, so you can add just this one. Mind you, it targets netcoreapp2.0.

 

Getting one's context


You will probably remember the HttpContext class from ASP.NET. The current instance of this class would represent the current context of execution, which included both the request information and the response channel. It was ubiquitous, even though in Web Forms it was sometimes hidden, it was the way by which the web application communicated with the client.

Of course, ASP.NET Core also has an HttpContext class, but there is a big difference, there is no longer a Current static property that lets us get hold of the current context, instead, the process is a bit more convoluted. Anyway, all of the infrastructure classes--controllers, views, view components, tag helpers, and filters, allow easy access to the current context.

So, besides Request and Response properties, which are mostly similar to their pre-core counterparts, we also have:

  • A Features collection, which exposes all of the features implemented by the current hosting server (Kestrel, WebListener/HTTP.sys, and more)
  • A RequestServices property, which gives us access to the built-in dependency injection framework, more on it in the next chapters
  • A TraceIdentifier property, which uniquely identifies a request, in ASP.NET Core 2.x; in earlier versions, we had to access it through a feature
  • A Connection object, from where we can obtain relevant information about the client connection such as any client certificates, for example:
    • The Authentication object, giving easy access to security primitives such as sign in, sign out, deny, and more
    • The Session object, which is implemented by the ISessionFeature feature, and is exposed directly by the HttpContext

The context is a vital part of an ASP.NET Core application, as we will see.

 

The OWIN pipeline


Previous versions of ASP.NET had a very close relation with Internet Information Services (IIS), Microsoft's flagship web server that ships with Windows. In fact, IIS was the only supported way to host ASP.NET.

Wanting to change this, Microsoft defined the Open Web Interface for .NET (OWIN) specification, which you can read about at http://owin.org. In a nutshell, it is a standard for decoupling server and application code, and for the execution pipeline for web requests. Because it is just a standard and knows nothing about the containing web server (if any), it can be used to extract away its features.

.NET Core borrowed heavily from the OWIN specification. There are no more Global.asax, web.config, or machine.config configuration files, modules or handlers. What we have is:

  • Some bootstrap code declares a class that contains a convention-defined method (Startup will be used, if no class is declared)
  • This conventional method, which should be called Configure, receives as its sole parameter a pointer to an IApplicationBuilder instance
  • You then start adding middleware to the IApplicationBuilder; this middleware is what will handle your web requests

A simple example is in order. First, the bootstrap class, which should probably be named Program:

public class Program
{
  public static void Main()
  {
    var host = new WebHostBuilder()
        .UseKestrel()
        .UseStartup<Startup>()
        .Build();
        host.Run();
  }
}

Things can get more complicated, but don't worry too much about it now. Later on, I will explain what this all means. For the time being, it's enough to know that we are leveraging a WebHostBuilder to host Kestrel, and passing a conventional class called Startup. This Startup class goes like this:

public class Startup
{
  public void Configure(IApplicationBuilder app)
  {
    app.Run(async (context) =>
    {
      await context.Response.WriteAsync("Hello, OWIN World!");
    }       
  }
}

There are a couple of things here that deserve an explanation. First, you will notice that the Startup class does not implement any interface or inherit from some explicit base class. This is because the Configure method does not have a predefined signature, other than its name, and taking as its first parameter an IApplicationBuilder. For example, the following is also allowed:

public void Configure(IApplicationBuilder app,
 IHostingEnvironment env) { /* ... */ }

This version even gives you more than what you asked for. But I digress.

The IApplicationBuilder interface defines a Run method. This method takes a RequestDelegate parameter, which is a delegate definition that accepts an HttpContext (remember this one?) as its sole parameter, and returns a Task. In my example, we made it asynchronous by adding async and await keywords to it, but it needs not be so. All you have to care about is to extract whatever you want from the HttpContext and write whatever you want to it, this is your web pipeline. It wraps both the HTTP request and response objects and we call it middleware.

The Run method is a full-blown pipeline on its own, but we can plug other steps (middleware) to the pipeline, by using the (pun intended) Use method:

app.Use(async (context, next) =>
{
  await context.Response.WriteAsync("Hello from a middleware!");
  await next.Invoke();
}       

This way, we can add multiple steps, and they all will be executed in the order they were defined:

app.Use(async (context, next) =>
{
  await context.Response.WriteAsync("step 1!");
  await next.Invoke();
}       
app.Use(async (context, next) =>
{
  await context.Response.WriteAsync("step 2");
  await next.Invoke();
}       

Just keep in mind that the order does matter here!

The Use method takes as its parameters an HttpContext instance and returns a Func<Task>, which is normally a call to the next handler, so that the pipeline proceeds.

We could extract the lambda to its own method, like this:

async Task Process(HttpContext context, Func<Task> next)
{
  await context.Response.WriteAsync("Step 1");
  await next.Invoke();
}

app.Use(Process);

It is even possible to extract the middleware to its own class, and apply it using the generic UseMiddleware method:

public class Middleware
{
  private readonly RequestDelegate _next;
  public Middleware(RequestDelegate next)
  {
    this._next = next;
  }
  public async Task Invoke(HttpContext context)
  {
    await context.Response.WriteAsync("This is a middleware class!");
    await this._next.Invoke(context);
  }
}

app.UseMiddleware<Middleware>();

In this case, the constructor needs to take as its first parameter a pointer to the next middleware in the pipeline, as a RequestDelegate instance.

I think you got the picture, OWIN defines a pipeline to which you can add handlers which are then called in sequence. The difference between Run and Use is that the former ends the pipeline, that is, it won't call anything after itself.

The following diagram (from Microsoft) clearly shows this:

The first middleware, in a way, wraps all of the next ones. For example, imagine you want to add exception handling to all the steps in the pipeline; you could do something like this:

app.Use(async (context, next) =>
{
  try
  {
    await context.Response.WriteAsync("inside an exception handler");
    await next.Invoke();
  }
  catch (Exception ex)
  {
    //do something with the exception
  }
  await context.Response.WriteAsync("outside an exception handler");
}   

The call to next.Invoke() is wrapped in a try...catch block, so any exception that may be thrown by another middleware in the pipeline, as long as it was added after this one, will be caught.

You can read more about Microsoft's implementation of OWIN at: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/owin.

Why is OWIN important? Well, because ASP.NET Core (and its MVC implementation) are built on it. We will see later that in order to have an MVC application we need to add the MVC middleware to the OWIN pipeline in the Startup class' Configure method, normally like this:

app.UseMvc();
 

Hosting


You probably noticed, when we talked about OWIN, that I mentioned that the sample app was hosted in Kestrel. Kestrel is the name of a platform-independent web server fully written in .NET Core (of course, using the native libraries of your operating system). You need to host your web application somewhere, and .NET Core offers the following options:

  • Kestrel: Platform independent, your host of choice in case you want to have your code run on any platform
  • WebListener: A Windows-only host, offering significant performance advantages over Kestrel, but also the disadvantage that it needs Windows; starting with ASP.NET Core 2, it is now called HTTP.sys
  • IIS: As in the past, you can continue to host your web app in IIS, on Windows, benefiting from the old pipeline and configuration tools

A server in this context is merely an implementation of IServer, an interface defined in the Microsoft.AspNetCore.Hosting NuGet package. This defines the base contract that a server offers, which can be described as:

  • A Start method, where all the fun begins, it is responsible for creating the HttpContext, setting up the Request and Response properties, and calling the conventional Configure method.
  • A collection of Features that are supported by the implementation. There are dozens of features, but, at the very least, a server needs to support IHttpRequestFeature and IHttpResponseFeature.

Each of these server implementations are provided in NuGet packages:

Server

Package

Kestrel

Microsoft.AspNetCore.Server.Kestrel

WebListener

Microsoft.AspNetCore.Server.WebListener (Microsoft.AspNetCore.Server.HttpSys from ASP.NET Core 2)

IIS

Microsoft.AspNetCore.Server.IISIntegration

 

IIS cannot be used on its own. IIS is, of course, a Windows native application and is therefore not available through NuGet, but the Microsoft.AspNetCore.Server.IISIntegration package includes the IIS ASP.NET Core Module, which needs to be installed in IIS so that it can run ASP.NET Core apps with Kestrel (WebListener is not compatible with IIS). There are, of course, other server implementations by third-party providers (take, as an example, Nowin, available at https://github.com/Bobris/Nowin).

So, what is there to know about these, and how can we select one of these hosting servers?

Kestrel

Kestrel is the default, multi-platform, web server. It offers acceptable performance, but lacks lots of features that are expected in real-life:

  • No buffering
  • No support for Windows authentication (as time passes, this is less of a problem)
  • No WebSockets
  • No HTTP/2
  • No direct file transmission
  • No strong security protection (large requests, and more)

From this, it should be clear that Kestrel is not meant to be used in production, unless sitting behind a reverse proxy (such as Nginx, Apache, or IIS). It is configured at bootstrap through the UseKestrel extension method and, if you need to configure its options, you will need to supply an additional lambda:

var host = new WebHostBuilder()
    .UseKestrel(opt =>
    {
      opt.ThreadCount = 10;
    })
    //rest goes here...

Read more about it at: https://docs.microsoft.com/en-us/aspnet/core/fundamentals/servers/kestrel.

WebListener

This one is for Windows only, as it is a wrapper around HTTP.sys, the Windows subsystem that handles web requests. It offers by far the best performance, supports HTTP/2, WebSockets, Windows Authentication, direct file transmission, port sharing, response caching and mostly anything that you can think of. The disadvantage, of course, is that it requires Windows 7 or Windows Server 2008 R2 and later. At bootstrap, use the UseWebListener extension method to add it to the host builder, possibly with a configuration parameter:

.UseWebListener(opt =>
    {
      opt.ListenerSettings.Authentication.AllowAnonymous = false;
    })

Note

As of ASP.NET Core 2.0, WebListener is now called HTTP.sys.

IIS

We already know about IIS. IIS can be used as a reverse proxy for Kestrel, or to add features that the host does not support, such as Windows Authentication. For that, we should include support for IIS, by calling UseIISIntegration. Here, the configuration should be done through the Web.config file, which, in this case, is a requirement (Visual Studio will add this file to the root of your project).

Nginx

Nginx is a UNIX and Linux reverse proxy that can be used with ASP.NET Core. We will talk a bit more about Nginx in Chapter 16, Deployment.

Apache

Apache, the popular UNIX and Linux server (which actually also runs in Windows) can also act as a reverse proxy. More info in Chapter 16, Deployment.

Configuration

As we've seen, usually, the server is chosen using a WebHostBuilder instance. As a minimum, you need to tell it which server to use and what is the root directory:

var host = new WebHostBuilder()
    .UseKestrel()
    .UseContentRoot(Directory.GetCurrentDirectory())
    .UseIISIntegration()
    .UseStartup<Startup>()
    .Build();

Features

Different servers will offer different features. Here are some of the features that are included out of the box:

Interface

Feature

IHttpRequestFeature

Access to the request object and collections (form, headers, cookies, query string, and more)

IHttpResponseFeature

Access to the response object and collections (headers, cookies, content, and more)

IHttpAuthenticationFeature

Authentication based on claims and principals

IHttpUpgradeFeature

Support for HTTP Upgrades (see https://tools.ietf.org/html/rfc2616.html#section-14.42)

IHttpBufferingFeature

Response buffering

IHttpConnectionFeature

Properties for local host calls

IHttpRequestLifetimeFeature

Detecting if a client has disconnected, and the ability to actually disconnect it

IHttpSendFileFeature

The ability to directly send a file as a response

IHttpWebSocketFeature

WebSockets

IHttpRequestIdentifierFeature

Uniquely identifying requests

ISessionFeature

Supplies the session functionality. Needs to be added by the session middleware, not available otherwise

ITlsConnectionFeature

Retrieving client certificates

ITlsTokenBindingFeature

Working with TLS tokens

 

All of these features can be obtained through the IServer's Features property or from the HttpContext, by requesting their interface. This is one way to obtain access to the functionality that the feature supplies, but for some features, there are workarounds. For example, the ASP.NET Session object can be obtained directly from the HttpContext. Features are essentially how the HttpContext class gets the behavior it exposes; for example, request and response objects, sessions, and more.

Launch configuration

Visual Studio can have more than one configuration per project, meaning, it can launch your project in several ways and there's a toolbar button that shows just this:

In particular, we can choose whether to launch our web application using IIS (or IIS Express) as the host, or use whatever (Kestrel or WebListener) is specified in the code. The launch settings are stored in the PropertieslaunchSettings.json file, which gets created by default by Visual Studio. This file has the following (or similar) contents:

{
  "iisSettings": {
    "windowsAuthentication": true,
    "anonymousAuthentication": true,
    "iisExpress": {
      "applicationUrl": "http://localhost:24896/", "sslPort": 0
    }
  },
  "profiles": {
    "IIS Express": {
      "commandName": "IISExpress",
       "launchBrowser": true,
       "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
    },
    "Web": {
      "commandName": "Project",
       "launchBrowser": true,
       "launchUrl": "http://localhost:5000",
       "environmentVariables": {
         "ASPNETCORE_ENVIRONMENT": "Development"
       }
    }
  }
}

Here we can see the default ports plus the environment name to be used (to be discussed shortly). This file does not need to be changed by hand (although it can), you can see it in visual form through project properties:

 

Inversion of control and dependency injection


Inversion of Control (IoC) and dependency injection (DI) are two related but different patterns. The first tells us that we should not depend on actual, concrete, classes, but instead on abstract base classes or interfaces that specify the functionality we're interested in. Depending on its registrations, the IoC framework will return a concrete class that matches our desired interface or abstract base class. DI on the other hand, is the process by which when a concrete class is built, the dependencies it needs are then passed to its constructor (constructor injection, although there are other options). These two patterns go very well together, and throughout the book, I will use the terms IoC or DI container/framework to mean the same thing.

.NET always had support for a limited form of inversion of control, Windows Forms designers used it at design time to get access to the current designer's services for example, and Windows Workflow Foundation also used it to get registered extensions at runtime. But in .NET Core, Microsoft centralized it and made it a first-class citizen of the ecosystem. Now virtually everything is dependent on the inversion of control and dependency injection framework. It is made available in the Microsoft.Extensions.DependencyInjection NuGet package.

An inversion of control and dependency injection container allow services (classes) to be registered and accessed by their abstract base class or an interface they implement. Application code does not need to care about what is the actual class that implements the contract, this makes it very easy to switch the actual dependencies in the configuration or at runtime. Other than that, it also injects dependencies into the actual classes that it is building. Say, for example, you have this scenario:

public interface IMyService
{
  void MyOperation();
}
public interface IMyOtherService
{
  void MyOtherOperation();
}
public class MyService : IMyService
{
  private readonly IMyOtherService _other;
  public MyService(IMyOtherService other)
  {
    this._other = other;
  }
  public void Operation()
  {
    //do something
  }
}

If you register a MyService class to the dependency injection container, when it builds an actual instance it knows that it will also need to build an instance of IMyOtherService to pass to the MyService constructor, and this cascades for every dependency in the actual IMyOtherService implementation.

The WebHostBuilder, when it is building the host, also initializes an IServiceCollection instance, which is then passed to the Startup class' ConfigureServices method. This is a conventional method that should be used for our own registrations.

Now, a service registration has three components:

  • The type under which it will be registered (the key of the registration)
  • The lifetime of it
  • The actual instance factory

A lifetime can be one of:

  • Scoped: A new instance of the service will be created per each web request (or scope), and the same instance will always be returned for the same request (scope) whenever we ask the DI framework for it
  • Singleton: The instance to be created will be kept in memory, and it will always be returned
  • Transient: A new instance will be created whenever it is requested

The instance factory can be:

  • An actual instance; of course, this cannot be used with the Transient or Scoped lifetimes
  • A concrete Type, which will then be instantiated as needed
  • A Func<IServiceProvider, object> delegate, that knows how to create instances of the concrete type, after receiving a reference to the dependency injection container

There are several extension methods that allow us to do registrations; all of the following are identical:

    services.AddScoped<IMyService, MyService>();
    services.AddScoped<IMyService>(sp => new MyService((IMyOtherService)
     sp.GetService(typeof(IMyOtherService))));
    services.AddScoped(typeof(IMyService), typeof(MyService));
    services.Add(new ServiceDescriptor(typeof(IMyService),
     typeof(MyService), ServiceLifetime.Scoped));

The same goes for all other lifetimes.

The dependency injection framework has the concept of scopes, to which scoped registrations are bound. We can create new scopes and have our services associated with them. There is the IServiceScopeFactory interface which is automatically registered and it allows us to do things like this:

var serviceProvider = services.BuildServiceProvider();
var factory = serviceProvider.GetService<IServiceScopeFactory>();

using (var scope = factory.CreateScope())
{
  var svc = scope.ServiceProvider.GetService<IMyService>();
}

Any scope-bound service returned from the service provider inside the CreateScope inner scope is destroyed with the scope. Interestingly, if any scope-registered service implements IDisposable, its Dispose method will be called at the end of the scope.

Now, you need to keep in mind a few things:

  • The same Type can only be registered once, it is not possible to have multiple registrations, even for different lifetimes
  • You cannot register a Singleton service that takes a dependency that is Scoped, as it wouldn't make sense, by definition Scoped changes every time
  • You cannot pass an instance to a Scoped or Transient registration
  • You can only resolve, from the factory delegate, services that have themselves been registered; the factory delegate, however, will only be called after all services have been registered, so you do not need to worry about the registration order
  • You can have several implementations registered for the same Type, and they will be returned in a call to GetServices<T>
  • The resolution will return null if no service by the given Type is registered
  • Only the last registered implementation for a given Type is returned by GetService/GetService<T>

Several .NET Core APIs supply extension methods that do their registrations; for example, AddMvc or AddSession.

After all the registrations are done, eventually, the actual dependency framework will be built from the IServiceCollection instance. It's public interface is none other than the venerableIServiceProvider, which has been around since .NET 1.0. It exposes a single method, GetService, which takes as its single parameter a Type to resolve. There are however, available in the Microsoft.Extensions.DependencyInjection package and namespace, a few useful generic extension methods:

  • GetService<T>(): Returns an instance of the service type already cast appropriately, if one is registered, or null otherwise
  • GetRequiredService<T>(): Tries to retrieve a registration for the given service type, and throws an exception if none is found
  • GetServices<T>(): Returns all of the services whose registration key matches (is identical, implements, is a subclass) of the given service key

You can register multiple services for the same Type, but only the last being registered will be retrievable using GetService(). Interestingly, all of them will be returned using GetServices()!

Note

Keep in mind that the latest registration for a Type overrides any previous one, meaning you will get the latest item when you do a GetService, but all of the registrations are returnable by GetServices.

Although the most common usage will probably be constructor injection, where the dependency injection framework creates a concrete type passing it all of its dependencies in the constructor, it is also possible to request at any given time an instance of the service we want, by a reference to a IServiceProvider, like the one available in the context:

var urlFactory =
  HttpContext.RequestServices.GetService<IUrlHelperFactory>();

Finally, I need to talk about something else. People have been using third-party dependency injection and inversion of control frameworks for ages. .NET Core, being as flexible as it is, certainly allows us to use our own, which may offer additional features to what the built in one provides. All it takes is that our DI provider of choice also exposes an IServiceProvider implementation; if it does, we just need to return it from the ConfigureServices method:

public IServiceProvider ConfigureServices(IServiceCollection services)
{
  //AutoFac
  var builder = new ContainerBuilder();
  //add registrations from services
  builder.Populate(services);
  return new AutofacServiceProvider(builder.Build());
}

All in all, it's very good to see inversion of control and dependency injection. This is just the basics, we will talk about dependency injection in pretty much all of the book.

 

Knowing the environments


.NET Core has the notion of environment. An environment is basically a runtime setting in the form of an environment variable called ASPNETCORE_ENVIRONMENT. This variable can take one of the following values (case-sensitive):

  • Development: A development environment, which probably does not need much explaining
  • Staging: A pre-production environment used for testing
  • Production: The environment (or as similar as possible) where the application will live once it is released

To be correct, you can pass any value, but these have particular significance to .NET Core. There are several ways by which you can access the current environment, but you're most likely to use one of the following methods, extensions methods and properties of the IHostingEnvironment interface:

  • IsDevelopment()
  • IsProduction()
  • IsStaging()
  • IsEnvironment("SomeEnvironment")
  • EnvironmentName

The IsDevelopment, IsProduction, and IsStaging extension methods are just convenience methods using the IsEnvironment method. Based on the actual environment you can make decisions on the code, such as pick a different connection string, web service URL, and so on. It is important to point out that this has nothing to do with Debug or Release compiler configurations.

You normally get an instance of IHostingEnvironment from the Configure method:

    public void Configure(IApplicationBuilder app,
     IHostingEnvironment env) { /* ... */ }

But also from the constructor of the Startup class:

    public Startup(IHostingEnvironment env) { /* ... */ }

Or even from the dependency injection framework, which is available from the HttpContext class, among other places:

    var env = 
      HttpContext.RequestServices.GetService<IHostingEnvironment>();

A final note, service configuration plays well with environments. Instead of a single ConfigureServices method, we can have multiple methods, named ConfigureDevelopmentServices, ConfigureStagingServices, and ConfigureProductionServices. A nice feature that can help us better organize our code!

 

A sample project


Throughout the book, I will be providing examples based upon a fictitious online shopping application: Book Store. This application will have the following basic requirements:

  • It shall have a public as well as a private section
  • Access to the private section shall be granted after successful authentication
  • It shall be possible to search the books in the database by simple criteria
  • A book shall have a picture together with its information and the number of ratings
  • It shall be possible for authenticated users to rate books
  • The application shall produce usage logs
  • It shall be translatable to different languages

The app will consist of ASP.NET Core code but also CSS, JavaScript, JSON, images and other files.

Structure

Our solution will be split by various projects, for better encapsulating our conceptual application layers:

  • Web: The core of the web app
  • DomainModel: The Entity Framework Core domain model
  • WebComponents: Reusable components
  • UnitTests: Unit tests

The following diagram describes the relationship between the different projects in the sample project:

The Web project shall contain the bootstrap code, controllers, views and static files.

The DomainModel and WebComponents projects can probably be shared by other projects, even some that may not be web applications but use the same domain model.

A sample domain model could be:

 

This will serve as the basis for the sample app we will be building. Here we have:

  • The Book is the main entity, it has the basic properties you'd expect in a book
  • A Book can have one or more Authors, possibly some Reviews and a few other properties
  • An Order will have a creation timestamp, a current state, and contain at least one OrderItem, but possibly more, and is associated with a User (the user will be managed by ASP.NET Identity, of which we'll see more in a few chapters)
  • A Review awards stars to a book and needs to be filed by a registered User

This should be more than enough for our purpose, which is to explain ASP.NET Core with a simple example.

 

Summary


In this first chapter, we went through some of the biggest changes in ASP.NET Core and .NET Core. I introduced you to some of the key concepts around .NET Core:

  • The NuGet distribution model
  • The OWIN pipeline
  • The hosting model
  • Environments
  • The improved context
  • The built-in dependency framework

Also, we had a brief introduction to the sample project that we will be developing throughout the book. In the course of the next chapters, we will dive more and more into it.

 

About the Author

  • Ricardo Peres

    Ricardo Peres is a Portuguese developer, blogger, and occasionally, an e-book author. He has over 17 years of experience in software development, using technologies such as C/C++, Java, JavaScript, and .NET. His interests include distributed systems, architectures, design patterns, and general .NET development.

    He currently works for the London-based Simplifydigital as a technical evangelist, and was first awarded as an MVP in 2015.

    Ricardo maintains a blog, Development With A Dot, where he regularly writes about technical issues.

    He wrote Entity Framework Core Cookbook - Second Edition and was the technical reviewer for Learning NHibernate 4 by Packt.

    Ricardo also contributed to Syncfusion's Succinctly collection of e-books with titles on NHibernate, Entity Framework Code First, Entity Framework Core, multitenant ASP.NET applications, and Microsoft Unity.

    Browse publications by this author

Latest Reviews

(10 reviews total)
Muito bom o livro muitos detalhes
Purchase worked well and without problems or delays.
i'm a reader from author's blog.

Recommended For You

Learning ASP.NET Core 2.0

Learn how web applications can be built efficiently using ASP.NET Core 2.0 and related frameworks

By Jason De Oliveira and 1 more
C# 8.0 and .NET Core 3.0 – Modern Cross-Platform Development - Fourth Edition

Learn the fundamentals, practical applications, and latest features of C# 8.0 and .NET Core 3.0 from expert teacher Mark J. Price.

By Mark J. Price
Hands-On Full-Stack Web Development with ASP.NET Core

Become a full-stack developer by learning popular Microsoft technologies and platforms such as .NET Core, ASP.NET Core, Entity Framework, and Azure

By Tamir Dresher and 2 more
Learn ASP.NET Core 3 - Second Edition

A beginner's guide to building fully functioning web applications from scratch using the latest features of ASP.NET Core 3 and C# 8

By Kenneth Yamikani Fukizi and 2 more