When I first heard about ASP.NET Core I couldn’t wait to try it. But for me, v1 and v1.1 just didn’t have enough APIs.

Welcome ASP.NET Core 2!

The ASP.NET Core 1 framework had around 14-16 thousand APIs available. With ASP.NET Core 2 that number is now in the region of 36 thousand.

With that I have started to move over some of my personal projects and wanted to jot down some of the biggest changes from ASP.NET.

Dependency Injection is baked in

When building any kind of large scalable application, dependency injection (DI) should be in the mix. With ASP.NET Core, Microsoft has taken away the pain of selecting and configuring DI. But don’t worry, you can still plugin and configure 3rd party implementations.

What do I need to do in order to use this fantastic feature? I hear you cry. Check out the code snippet below.

public void ConfigureServices(IServiceCollection services)
{
    services.AddTransient<ITransientService, TransientService>();
    services.AddScoped<IScopedService, ScopedService>();
    services.AddSingleton<ISingletonService, SingletonService>();
}

That’s it!

The only effort is in selecting the lifetime for the service being registered.

This is achieved by using either the AddTransient, AddScoped or AddSingleton methods.

  • AddTransient - A new instance of the service will be provided to each class the requests it
  • AddScoped - One instance of the service will be created per request
  • AddSingleton - One instance of the service will be created for the lifetime of the application

New csproj file

The .csproj file has been overhauled in Asp.Net Core 2. It’s been greatly simplified from the .csproj file we are all used to in ASP.NET projects.

The biggest difference has come from the change to a folder based project structure. Files now no longer have to be explicitly included in the solution. I’m really loving this as I would hate to know the number of merge conflicts I have had to resolve due to new files being added to a project.

Microsoft has removed the GUID-based references to other projects in the solution. This makes the file much more readable. You can now edit the project file on the fly, without having to unload the project and then reload it.

Razor Pages

Think MVC view but without a controller.

To define a razor page use the @page directive at the top of the page.

@page

<h1>Page Title</h1>

<div>
 ...
</div>

Here I’ve defined a razor page using the @page directive I mentioned before. This directive turns the page into a MVC action. Once this happens the razor page will handle requests directly without the need for a controller.

It’s important to note that the @page directive must be the first thing declared on a page. Once declared it will affect any razor constructs used on the page.

A step further

The above example is a very simple use case for razor pages. A step further is to use a code-behind file.

I’m sure some of you are thinking what I did… WebForms!!

But don’t worry, there is no ViewState rearing its ugly head. Let me show you an example.

(pages/HelloWorld.cshtml.cs)

using System;
using Microsoft.AspNetCore.Mvc.RazorPages;

namespace AspNetCoreExamples.RazorPages
{
    public class HelloWorldModel : PageModel
    {
        public string Heading { get; private set; } = "Hello World";
    }
}
(pages/HelloWorld.cshtml)

@page
@using RazorPages
@model HelloWorldModel

<h1>@Model.Heading</h1>

There is a convention with naming which I’ve demonstrated above. The PageModel file has the same name as the Razor Page but with .cs on the end.

Routing

By default the runtime will look for Razor Pages in a folder called pages. From that starting point URLs will reflect the folder structure.

/pages/index.cshtml is returned when requesting / or /index./pages/hello-world/index.cshtml is returned when requesting /hello-world or /hello-world/index.

You get the idea.

There is a lot more to Razor Pages which I’ll cover in a future post but I’ll leave it here for now.

Global.asax is gone

ASP.NET applications are bootstrapped using the Global.asax. In this file routes, filters and bundles are some of the common items registered. Then came OWIN which changed the way an application is bootstrapped. It introduced a startup.cs file where middleware could be plugged in and configured. This was in an attempt to decouple the application from the server.

In ASP.NET Core the global.asax file is now removed completely. And while there is still a startup.cs file. The dependency on OWIN has also been removed. Bootstrapping is now the responsibility of the program.cs file. Just as in a console application the program.cs contains a Main method. This is now what is used to load the startup.cs file.

public class Program
{
    public static void Main(string[] args)
    {
        var host = new WebHostBuilder()
            .UseKestrel()
            .UseContentRoot(Directory.GetCurrentDirectory())
            .UseIISIntegration()
            .UseStartup<Startup>()
            .Build();

        host.Run();
    }
}

Storing App Settings

In ASP.NET it is common to place certain application settings in the <appSettings> section of the web.config. This is no longer the case in ASP.NET Core.

ASP.NET Core can load data from any file. By default a new application is setup to use a file called appsettings.json. By default the file will have some like this in it.

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  }
}

To add your own settings information into the file you simple create a new object.

{
  "Logging": {
    "IncludeScopes": false,
    "LogLevel": {
      "Default": "Warning"
    }
  },
  "Api" {
    "BaseUrl": "http://domain.com/api"
  }
}

In order to use a setting you the settings file needs to be loaded in Startup.cs.

public Startup(IHostingEnvironment env)
{
    var builder = new ConfigurationBuilder()
        .SetBasePath(env.ContentRootPath)
        .AddJsonFile("appsettings.json", optional: false, reloadOnChange: true)
        .AddEnvironmentVariables();
    Configuration = builder.Build();
}

public IConfigurationRoot Configuration { get; }

You can then access any setting inside by doing the follows.

var baseUrl = Configuration.GetSection("Api")["BaseUrl"];