In continuation of the article where we discussed what is more appropriate for Docker containers: .NET Core or .NET Framework, let’s take a closer look at ASP.NET Core.

Being a .NET development company, we have an understanding that .NET Core and ASP.NET Core are two independent technologies that are alike and different like the .NET Framework and ASP.NET. In this article, we will review the history, advantages, and disadvantages of both ASP.NET Core and .NET Core. On top of that, we will go through some changes that occurred to the ASP.NET Core project architecture, based on ASP.NET programmers review.

What Are .NET Core and ASP.NET Core, and Why Were They Created?

Millions of developers used and still use ASP.NET 4.x to create web applications. It is a great technology that has a long history of development, going back to its first release in early 2002. A lot has been changed since then to match these changes, including the framework itself. There are at least a few major reasons that resulted in the creation of a new framework from scratch.

Firstly, many years Microsoft has been providing closed source software, and this deterred many developers who were the supporters of open source. Some of the Microsoft partners had access to the source code, which is obviously not the same as open source. Even if you do not contribute to the source code, access to it can help you to clearly understand how exactly the framework works and what the particular system function does. It also allows the community to contribute to the development process: easy improvements, quick discussion, problems solving. It is what the developers did not have before.

Secondly, .NET framework supports a particular platform, so no wonder that people associate it with Windows only. This fact also was a great downside for people, who cannot or do not want to use the Windows operating system.

Thirdly, .NET Framework has gradually refused the idea of completely separating side-by-side installations by having no separation between minor version and sometimes even major versions. For example, applications with two different versions of the .NET Framework will coexist with the risk of sharing dependencies, which can cause problems. As for ASP.NET, which is based on System.Web.dll, its architecture closely reflects the way the IIS processes requests. That is why the segregation of ASP.NET from IIS is nearly impossible today. System.Web also contains many features in one module, which makes it hard to modularize functionality and change the system-level behavior of ASP.NET. You cannot get only a part of the functionality that you need — you get all in one or get nothing.

All these reasons pushed to rethink and change the approach of how the .NET platform should move forward. That is how .NET Core and ASP.NET Core were created. They both have a Core in the name for a reason. It was done to avoid the impression that it was just a new update of the .NET framework and to emphasize their idea to change the concept itself. Despite all these facts, it does not make your existing skills outdated. It is just a major investment in the future of the whole platform.

ASP.Net Core Benefits

As mentioned above, ASP.NET Core is a new, open source, modular, cross-platform, extensible, asynchronous, and a much leaner framework. It is now the only framework that runs on top of either .NET Core 5 runtime (Core-CLR) or the .NET Framework runtime (CLR) and has a lot of advantages, most of which we will review below.

NuGet

In contrast to the monolithic .NET framework, .NET Core platform is as a set of NuGet packages that provide a small, discrete piece of functionality. It allows you to optimize applications, make them more lightweight and much leaner. You can also ship a private version of the .NET Core Framework for your specific application without any risk that other application versions can change your application’s behavior.

That is a key worth of ASP.NET Core, which is no longer based on System.Web.dll. It can run on multiple versions of .NET Core on the same machine. NuGet allows ASP.NET team to provide new functionality and fixes much easier and faster. In this way, if Microsoft provides an upgrade to any of the packages, you can just upgrade it and that is it. With ASP.NET Core 2.1, Microsoft has introduced a metapackage called Microsoft.AspNetCore.App, which contains all the libraries shipped by the .NET and ASP.NET teams, and provides direct support without locking you into specific versions of the third-party dependencies. All of this improves the performance and scalability of applications.

Open Source

.NET Core and ASP.NET Core are now open sources. It is a great step up for the whole .NET community because it makes the development process transparent and clean, allowing developers to be involved in code review, bug fixing, providing new features, and an opportunity to study the used libraries in detail. One implies the other, and open source enables Microsoft to extend the .NET unification to cross-platform development as well. .NET Core is having a single code base that can be used to build and support all the platforms, including Windows, Linux, and Mac OS. Obviously, some individual components, such as OS specific file system, require a separate implementation. The delivery model through NuGet allows to abstract those differences away.

Single API

The important part of this for developers is that it is a single API that runs on different platforms. They do not need to take care of it as the package already contains different implementations for each of the environments. Configuration features also became more cross-platform-friendly by replacing the old ones with JSON or INI files and environment variables. Cross-platform also means that you have a more extensive range of operating systems that you can use for deployment because Azure will support ASP.NET Core in both Linux and Windows VMs. It’s up to you what to choose.

Visual Studio Code

As a great plus for those who do not want to install Visual Studio , now you can also choose Visual Studio Code, Atom, Emacs, or even work with command prompt. For example, Visual Studio Code is the cross-platform code editor for Linux, MacOS, and Windows. T allows you to build, debug and run ASP.NET Core web applications, Node web application or .NET Core console applications with the ability to use IntelliSense, code completion, and Git integration. If you are a command-line-tools partisan, you will be glad to hear that everything that is involved in building, compiling or running .NET Core applications can be done using the command line.

Performance Benefits

Due to ASP.NET Core smaller footprint, there are also some performance benefits that are specific to .NET Core, but most of them are applied to both the .NET Framework and .NET Core.

As for ASP.NET and IIS ligament, now ASP.NET Core actually does not require IIS to run. It goes with the following server implementations:

Kestrel is the default, cross-platform HTTP server for ASP.NET Core;

IIS HTTP Server (IISHttpServer) is an IIS in-process server implementation used with the ASP.NET Core Module;

HTTP.sys is a Windows-only HTTP server based on the HTTP.sys kernel driver and HTTP Server API.

The most popular among them is Kestrel and it is also a default one in ASP.NET Core templates. When you create a new project in Visual Studio, it is automatically configured to run in Kestrel. The latter is actually based on libuv, which is used for I/O work and supports running multiple event loops. However, despite being fast, cross-platform and having been optimized for throughput performance, it does not provide all functionality, as a full-featured Web service (like IIS) provides no port sharing, easy SSL configuration, and URL rewrites. Some of these features may appear in the future, but today Kestrel is normally used with a reverse proxy server, such as IIS, Nginx, or Apache. It is a plus and a minus at the same time. Kestrel is really fast, in fact, six times faster than Node.js for static and plain text operations, and it provides better request processing performance, but all of this is achieved due to it is not а feature-rich web server. So, why was it needed to create such a web server if you still need to use a proxy server? Because without it, you are not able to start your application on different platforms without changes. Without Kestrel on other cross-platform web servers, the ASP.NET Core application code would have changed for each of these web servers to match their startup criteria.

As you can see, ASP.NET Core is a major change. You still can use frameworks like MVC, Web API, SignalR, and their syntax is not going to change extremely, as well as business logic written using Entity Framework. It is also still the same C#, F#, etc. code, and the .NET Framework is behind these all. In the end, the ultimate goal was not to replace .NET Framework with .NET Core but to add an alternative for developers, engage the new community, provide a brand-new stack to fit new changes in the development, and extend the platform in the future.

ASP.NET Core Cons

Some of the .NET Framework technologies are not available in the current version of .NET Core. Actually, some of them are planned to be available with later releases, but others might never come out. Below is a short list of the scenarios when you cannot use .NET Core:

ASP.NET Web Forms and ASP.NET Web Pages. They are only supported and provided by the full .NET Framework. In the GitHub thread you can find an answer from one of the Microsoft developers who explained that it was not planned to port WebForms to ASP.NET Core.

WCF services implementation. This scenario might be considered for further releases of .NET Core, but now, the most accurate answer would be “No, currently it is not available for .NET Core”. The only available library is WCF-Client, which is suitable for “mobile devices or on mid-tier servers to communicate with existing WCF services”, as their GitHub page says.

Workflow-related services including Windows Workflow Foundation (WF), Workflow Services (WCF + WF in a single service), and WCF Data Services (formerly known as ADO.NET Data Services). There are currently no plans to bring them to .NET Core.

Windows Forms and Windows Presentation Foundation (WPF) applications are not supported yet. However, the great news is that earlier this year Microsoft announced that they planned first preview release of .NET Core 3 near the end of this year and the final one in 2019. This new release will include an ability to run new and existing Windows desktop applications on .NET Core. and will allow you to enjoy all the benefits that .NET Core has to offer. Support for Windows desktop will be added as a set of “Windows Desktop Packs”, which will only work on Windows.

Missing 3rd party library support. .NET Core 2.0 provides a compatibility shim between the .NET Framework and .NET Core, but you may still have some issues with compatibility if the class library uses any .NET Framework APIs that are not supported.

You cannot use Windows-specific APIs in ASP.NET Core and .NET Core since these frameworks are designed to be more independent from the operating system. For example, you cannot use System.Drawing namespace or work with Windows Registry, for this you should use the .NET Framework.

There are a lot of discussions in GitHub threads on the subject of porting .NET Framework libraries to .NET Core, and this is a really good example of how open source may impact project development. If you open these threads, you will see that on each issue Microsoft employees answer about plans of porting technologies, discuss the purposes why they needed this porting to know what exactly is necessary for developers. Obviously, there will be technologies that will not be ported at all, since they are related to Windows operating system when .NET Core was made to provide cross-platform support.

Now, when we have reviewed all major changes in the .NET Core and ASP.NET Core let us move towards a new application structure and changes, which persist in a new type of the project.

ASP.NET Core Fundamentals

In this paragraph, we will create a test project to review changes in the structure on a real example. Assume that you have at least once created a new project with Visual Studio, and we will review an option of creating a new solution with the project together by using a console.

To create a solution, open the console (we will use Windows PowerShell, but you can use command prompt as well) and run the next command:

dotnet new sln -n reviewCoreApp 1 2 3 dotnet new sln - n reviewCoreApp

Here dotnet new is a command with which you can create a new project, configuration file, or a solution based on the specified template with all needed dependencies. In it we indicated that we want to create a Solution with an option sln and added an option -n to set the name of the project. If you do not specify the name it will be automatically set to the equivalent name of the folder. You can also use command dotnet new -l to get a list of all available templates. After running this command, if you open the folder, you will see a newly created solution. Related to the project standard structure, create a new folder near the solution in any way:

manually in the file explorer;

in the PowerShell with command md CoreAppProj;

in the Linux console with command mkdir CoreAppProj.

Enter into this new directory and create the project with the command

dotnet new mvc 1 2 3 dotnet new mvc

Option “mvc” means that the project type will be “ASP.NET Core Web App (Model-View-Controller)” and the name will be set to the folder name. To link the solution and the project return back to the solution folder and run next command:

dotnet sln add .\CoreAppProj\CoreAppProj.csproj 1 2 3 dotnet sln add . \ CoreAppProj \ CoreAppProj . csproj

If you now open the project in the Visual Studio you will see a standard created project:

If you review the project files carefully, you will notice, that the .csproj file format has been simplified in ASP.NET Core. Let us review the main changes step by step.

Program.cs

In ASP.NET Core, the entry point to an application is Startup.cs, and you no longer have a dependency on Global.asax. ASP.NET Core handles the entry through the Program.cs Main method (similar to console applications and ASP.NET Core application is actually a console application) and Startup is loaded through there:

using Microsoft.AspNetCore; using Microsoft.AspNetCore.Hosting; namespace CoreAppProj { public class Program { public static void Main(string[] args) { CreateWebHostBuilder(args).Build().Run(); } public static IWebHostBuilder CreateWebHostBuilder(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup<Startup>(); } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using Microsoft . AspNetCore ; using Microsoft . AspNetCore . Hosting ; namespace CoreAppProj { public class Program { public static void Main ( string [ ] args ) { CreateWebHostBuilder ( args ) . Build ( ) . Run ( ) ; } public static IWebHostBuilder CreateWebHostBuilder ( string [ ] args ) = > WebHost . CreateDefaultBuilder ( args ) . UseStartup < Startup > ( ) ; } }

The “Main” method uses WebHostBuilder, which follows the builder pattern to create a web application host. The builder includes:

the method that initializes a new instance of the WebHostBuilder class with pre-configured defaults – CreateDefaultBuilder(args);

the method that defines the startup class – UseStartup (). When you start the application, the ASP.NET environment will look for a class in the application assembly called Startup and load it. Name “Startup” is the default name, but you can rename it as you wish. Only standard construction of this class is required.

As we mentioned above, the default server should be Kestrel and you often can meet the following realization:

using Microsoft.AspNetCore.Hosting; namespace CoreAppProj { public class Program { public static void Main(string[] args) { var host = new WebHostBuilder() .UseKestrel() .UseStartup<Startup>() .Build(); host.Run(); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 using Microsoft . AspNetCore . Hosting ; namespace CoreAppProj { public class Program { public static void Main ( string [ ] args ) { var host = new WebHostBuilder ( ) . UseKestrel ( ) . UseStartup < Startup > ( ) . Build ( ) ; host . Run ( ) ; } } }

Both of them (realization above and in our example) aregood. You just need to know that using “CreateDefaultBuilder” method means that the next default settings will be applied (provided by official Microsoft documentation):

use Kestrel as the web server and configure it using the application’s configuration providers;

set the ContentRootPath to the result of GetCurrentDirectory();

load IConfiguration from “appsettings.json” and “appsettings.[EnvironmentName].json”;

load IConfiguration from User Secrets when EnvironmentName is ‘Development’ using the entry assembly;

load IConfiguration from environment variables;

configures the ILoggerFactory to log to the console and debug output;

enables IIS integration;

enables the ability for frameworks to bind their options to their default configuration sections.

You can manually change the parameters and set another web server. The Build and Run methods build the IWebHost that will host the app and start it listening for incoming HTTP requests.

Startup.cs

The UseStartup method on WebHostBuilder defines the Startup class of the application, where application’s request pipeline is defined and where all services are configured. A startup must include a Configure method where the necessary middleware is added to the pipeline. Below is a generated piece of code that you should see after creating a project:

using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; namespace CoreAppProj { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } // 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(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseHttpsRedirection(); app.UseStaticFiles(); app.UseCookiePolicy(); app.UseMvc(routes => { routes.MapRoute( name: "default", template: "{controller=Home}/{action=Index}/{id?}"); }); } } } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 using Microsoft . AspNetCore . Builder ; using Microsoft . AspNetCore . Hosting ; using Microsoft . AspNetCore . Http ; using Microsoft . AspNetCore . Mvc ; using Microsoft . Extensions . Configuration ; using Microsoft . Extensions . DependencyInjection ; namespace CoreAppProj { public class Startup { public Startup ( IConfiguration configuration ) { Configuration = configuration ; } public IConfiguration Configuration { get ; } // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices ( IServiceCollection services ) { services . Configure < CookiePolicyOptions > ( options = > { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options . CheckConsentNeeded = context = > true ; options . MinimumSameSitePolicy = SameSiteMode . None ; } ) ; services . AddMvc ( ) . SetCompatibilityVersion ( CompatibilityVersion . Version_2_1 ) ; } // 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 ( ) ; } else { app . UseExceptionHandler ( "/Home/Error" ) ; app . UseHsts ( ) ; } app . UseHttpsRedirection ( ) ; app . UseStaticFiles ( ) ; app . UseCookiePolicy ( ) ; app . UseMvc ( routes = > { routes . MapRoute ( name : "default" , template : "{controller=Home}/{action=Index}/{id?}" ) ; } ) ; } } }

ConfigureServices defines the services used by the application and Configure defines the middleware in the request pipeline. In the example above, Configure method configures the pipeline with support for:

Error pages;

HTTP Strict Transport Security;

HTTP redirection to HTTPS;

ASP.NET Core MVC.

Middleware

ASP.NET Core middlewares are the pieces of code that perform asynchronous logic on an HttpContext. Incoming requests are passed through the pipeline where each middleware invokes the next middleware in the pipeline or terminates the request. Middleware can perform any kind of operations, such as handling authentication, errors, static files, etc. For example, MVC in ASP.NET Core is also implemented as middleware. Actually, you can either create custom middleware or use the needed one from a huge set of built-in middleware, and you can write your own custom middleware. In the example above the middlewares are:

UseDeveloperExceptionPage – allows seeing detailed exception information. It is strongly recommended to use it only on staging. Call of this middleware should be placed before the middleware, where you want to catch exceptions, for example, before app.UseMvc();

UseExceptionHandler – configures an exception handler page. Usually, is used on production instead of UseDeveloperExceptionPage, so when the user gets an error, will be shown the page, which you specify in this middleware;

UseHsts – is used to send HTTP Strict Transport Security Protocol (HSTS) headers to clients;

UseHttpsRedirection – is used to redirect HTTP requests to HTTPS;

UseStaticFiles – the parameterless UseStaticFiles method enables functionality to access files in the folder through the web browser. For example, you can include the path to any file from the wwwroot folder into the markup and it will be visible to users. The folder wwwroot ( /wwwroot) is the default directory, but it can be changed manually;

UseCookiePolicy – enables cookie policy capabilities in an app. Also only affects components registered after it in the pipeline.

Configuration

ASP.NET Core also changed its configuration model for handling simple name-value pairs. The new configuration model is based on key-value pairs established by configuration providers instead of relying on System.Configuration or web.config. Configuration providers can read configuration data from a variety of file formats: XML, JSON, INI as well as from environment variables, command line arguments, or an in-memory collection. You can also write your own custom configuration provider. By default, in your test project, which we created above, you should see automatically created JSON file with a name “appsettings.json”. You can add connection string in it, set allowed hosts and proceed.

Environment Variables Configuration Provider

New configuration model also allows using environment variables via EnvironmentVariablesConfigurationProvider, which loads configuration from environment variable key-value pairs at runtime. To activate the environment variables configuration, you should add call the AddEnvironmentVariables extension method on an instance of ConfigurationBuilder. To activate the environment variables configuration, you should add call the AddEnvironmentVariables extension method on an instance of ConfigurationBuilder. When the application will run, the Environment Variables Configuration Provider will be called right after configuration is established from user secrets and appsettings files. Calling the provider in this position allows the environment variables to read at runtime to override configuration set by user secrets and appsettings files.

Running the Application

At the beginning of the section, we have already briefly reviewed the ways to start the development of applications by using .NET Core CLI tools, so now you must have a project for which we will review few more commands to finish this rough overview tutorial.

If you start looking for any information about running an application via CLI, you can find at least 3 commands:

dotnet restore – it calls into NuGet, which analyzes the CoreAppProj.csproj file, downloads the dependencies defined in the file (or grabs them from a cache on your machine), and writes the obj/project.assets.json file, which is necessary to compile and run the code;

dotnet build – builds a project and all of its dependencies;

dotnet run – runs source code.

Actually, if you are using the .NET Core 2.0 SDK, you will need to use only the last command from this list. dotnet restore command runs implicitly by all commands that require a restart : dotnet new, dotnet build and dotnet run. This command is still being used in certain cases, but in this particular one, it is not required. As for the dotnet build, it is also called by the command dotnet restore to ensure that the build targets have been built. That is why, if we run dotnet run command, the project dependencies are downloaded, the project is build and the application runs. And that is all! Now you can start using these basic concepts to create an application and improve your skills.

Conclusion

.NET Core and ASP.NET Core include many new enhancements and fundamental changes that should help you in everyday development. Would you believe if someone in the past said Microsoft would provide open source and cross-platform solutions? Probably, not. But times change, and today Microsoft provides great solutions that attract more and more developers. That is why we believe that the future holds even bigger changes that we will cover in other articles.