Rick Strahl's Web Log

Wind, waves, code and everything in between...
ASP.NET • C# • HTML5 • JavaScript • AngularJs
Contact   •   Articles   •   Products   •   Support   •   Search
Ad-free experience sponsored by:
ASPOSE - the market leader of .NET and Java APIs for file formats – natively work with DOCX, XLSX, PPT, PDF, images and more

.NET Core 2.0 and ASP.NET Core 2.0 are Here

continued from Page 1

Easier ASP.NET Startup Configuration

Another nice improvement and a sign of growing up is that the ASP.NET Core startup code in 2.0 is a lot more streamlined and there's quite a bit less of it.

The absolute minimal ASP.NET Web application you can build is just a few lines of code:

public static void Main(string[] args)
    // The simplest thing possible!
    WebHost.Start(async (context) =>
        await context.Response.WriteAsync("Hello World. Time is: " + DateTime.Now);

Notice that this code works without any dependencies whatsoever, and yet has access to an HttpContext instance - there's no configuration or additional setup required, the framework now uses a set of common defaults for bootstrapping an application. Hosting options, configuration, logging and a few other items are automatically set with common defaults, so these features no longer have to be explicitly configured unless you want to change the default behavior.

The code automatically hooks up hosting for Kestrel and IIS, sets the startup folder, allows for host url specification and provides basic configuration features - all without any custom configuration required. All of these things needed to be configured explicitly previously. Now - all of that is optional. Nice!

To be realistic though, if you build a real application that requires configuration, authentication, custom routing, CORS etc. those things still have to be configured and obviously that will add code. But the point is that ASP.NET Core now has a default configuration that out of box lets you get stuff done without doing any configuration.

The more common configuration setup looks like this:

public static void Main(string[] args)
    var host = WebHost.CreateDefaultBuilder(args)

with a Startup configuration class that handles minimal configuration for an MVC/API application:

public class Startup
    public void ConfigureServices(IServiceCollection services)

    public void Configure(IApplicationBuilder app, IHostingEnvironment env, IConfiguration configuration)
        app.UseMvcWithDefaultRoute();  // use only AttributeRoutes

You can then use either RazorPages (loose Razor files that can contain code) or standard MVC or API controllers to handle your application logic.

A controller of course is just another class you create that optionally inherits from Controller or simply has a Controller postfix:

public class HelloController 
    public object HelloWorld(string name)
        return new
            Name =  name,
            Message = $"Hello World, {name}!",
            Time = DateTime.Now

In short, basic configuration for a Web application is now a lot cleaner than in 1.x versions.

One thing that has bugged me in ASP.NET Core is the dichotomy between the ConfigureServices() and Configure() methods. In 1.x ASP.NET Core seemed to have a personality crisis in where to put configuration code for various components. Some components configured in ConfigureServices() using the AddXXX() methods, others did it in the Configure() method using the UseXXX() methods. In 2.0 Microsoft seems to have moved most configuration behavior into ConfigureServices() using options objects (via Action delegates that actually get called later in the pipeline), so now things like CORS, Authentication and Logging all use a similar configuration patterns.

So for example, in the following code, DbContext, Authentication, CORS and Configuration are all configured in the ConfigureServices() method:

 public void ConfigureServices(IServiceCollection services)
    services.AddDbContext<AlbumViewerContext>(builder =>
        string useSqLite = Configuration["Data:useSqLite"];
        if (useSqLite != "true")
            var connStr = Configuration["Data:SqlServerConnectionString"];
            // Note this path has to have full  access for the Web user in order 
            // to create the DB and write to it.
            var connStr = "Data Source=" +
                          Path.Combine(HostingEnvironment.ContentRootPath, "AlbumViewerData.sqlite");

		.AddCookie(o =>
			o.LoginPath = "/api/login";
			o.LogoutPath = "/api/logout";
	services.AddCors(options =>
            builder => builder

	// Add Support for strongly typed Configuration and map to class

The Configure() method generally then only enables the behaviors configured above by using various .UseXXXX() methods like .UseCors("CorsPolicy"), .UseAuthentication(), UseMvc().

While this still seems very disjointed at least the configuration logic is now mostly kept in a single place in ConfigureServices().

Ironically I've been struggling with this same issue in porting another library - Westwind.Globalization - to .NET Core 2.0 and I needed to decide how to configure my component. I chose to follow the same pattern as Microsoft using ConfigureServices with an Action delegate that handles option configuration:

services.AddWestwindGlobalization(opt =>
    // Resource Mode - Resx or DbResourceManager                
    opt.ResourceAccessMode = ResourceAccessMode.DbResourceManager;  // ResourceAccessMode.Resx

    opt.ConnectionString = "server=.;database=localizations;integrated security=true;";
    opt.ResourceTableName = "localizations_DEVINTERSECTION10";
    opt.ResxBaseFolder = "~/Properties/";

    // Set up security for Localization Administration form
    opt.ConfigureAuthorizeLocalizationAdministration(actionContext =>
        // return true or false whether this request is authorized
        return true;   //actionContext.HttpContext.User.Identity.IsAuthenticated;


implemented as an extension method with an Action delegate:

public static IServiceCollection AddWestwindGlobalization(this IServiceCollection services,
            Action<DbResourceConfiguration> setOptionsAction = null) 
    // add additional services to DI
    // configure based on options passed in

I'm not a fan of this (convoluted) pattern of indirect referencing and deferred operation, especially given that ConfigureServices() seems like an inappropriate place for component configuration when there's a Configure() method where I'd expect to be doing any configuring...

But I have to admit that once you understand how Microsoft uses the delegate-option-configuration-pattern, and if you can look past the consitent inconsistency, it is easy to implement and work with so I'm not going to rock the boat and do something different.

IRouterService - Minimal ASP.NET Applications

MVC or API applications typically can be built using the MVC framework. As you've seen above it's a lot easier with 2.0 to get an API application configured and up and running. But MVC has a bit of overhead internally.

If you want something even simpler perhaps for a quick one off minimal Micro Services, or you are a developer that wants to build a custom framework on top of the core ASP.NET middleware pipeline, you can now do that pretty easily by taking advantage of IRouterService.

Here's another very simple single file self contained ASP.NET application that returns a JSON response of a routed request:

public static class Program
    public static void Main(string[] args)

            .ConfigureServices(s => s.AddRouting())
            .Configure(app => app.UseRouter(r =>
                r.MapGet("helloWorldRouter/{name}", async (request, response, routeData) =>
                    var result = new
                        name = routeData.Values["name"] as string,
                        time = DateTime.UtcNow
                    await response.Json(result);
                r.MapPost("helloWorldPost" async (request, response, routeData) => {

    public static Task Json(this HttpResponse response, object obj, 
                            Formatting formatJson = Formatting.None)
        response.ContentType = "application/json";

        JsonSerializer serializer = new JsonSerializer
            { ContractResolver = new CamelCasePropertyNamesContractResolver() };
        serializer.Formatting = formatJson;

        using (var sw = new StreamWriter(response.Body))
        using (JsonWriter writer = new JsonTextWriter(sw))
            serializer.Serialize(writer, obj);                

        return Task.CompletedTask;

The key here is the router service that lets you directly map URLs to actions that have a request and a response you read from and write to. This is obviously a bit more low level than using MVC/API controllers. There's no HttpContext and you have to handle serializing inbound and outbound data yourself. But it gives you complete control over request handling and the ability to create very, very small services with minimal overhead.

With a few simple helper extension methods you can provide a lot of functionality using just this very simple mechanism. This is very cool if publishing simple one of 'handlers'. It can also be a good starting point if you ever want to build your own custom not-MVC MVC Web framework 😃

IRouterService functionality is primarily for specialized use cases where you need one or more very simple notification requests. It is very similar to where you might employ serverless Web Functions (like Azure Functions, AWS Lambda) for handling simple service callbacks or other one off operations that have few dependencies.

I've also found IRouterService useful for custom route handling that doesn't fall into the application space, but is more of an admin feature. For example, recently I needed to configure an ASP.NET Core app to allow access for Let's Encrypt's domain validation callbacks and I could just use a route handler to handle a special route in the server's Configure() code:

//app.UseRouter(r =>
    r.MapGet(".well-known/acme-challenge/{id}", async (request, response, routeData) =>
        var id = routeData.Values["id"] as string;
        var file = Path.Combine(env.WebRootPath, ".well-known","acme-challenge", id);
        await response.SendFileAsync(file);


Http.Sys Support

For Windows, ASP.NET Core 2.0 now also supports Http.sys as another Web server in addition to the Kestrel and IIS/IIS Express servers that are supported by default. http.sys is the kernel driver used to handle HTTP services on Windows. It's the same driver that IIS uses for all of its HTTP interaction and now you can host your ASP.NET Core applications directly on Http.sys usig the Microsoft.AspNetCore.Server.HttpSys package.

The advantage of using Http.sys directly is that it uses the Windows http.sys infrastructure which is a hardened Web Server front end that supports high level support for SSL, content caching and many security related features not currently available with Kestrel.

For Windows the recommendation has been to use IIS as a front end reverse proxy in order to provide features like static file compression and caching, SSL management and rudimentary connection protections against various HTTP attacks against the server.

By using the Httpsys server you can get most of these features without having to use a reverse proxy to front Kestrel which has a bit of overhead.

To use HttpSys you need to explicitly declare it using the .UseHttpSys() configuration added to the standard startup sequence (in program.cs):

        .UseHttpSys(options =>
            options.Authentication.Schemes = AuthenticationSchemes.None;
            options.Authentication.AllowAnonymous = true;
            options.MaxConnections = 100;
            options.MaxRequestBodySize = 30000000;

And then configure the local port in order for it to be accessibly both locally and remotely (by opening up a port on the firewall). When you do you should now see the HTTP Sys server:

Figure 6 - The http.sys hosting in ASP.NET Core provides efficient Windows server hosting without a proxy front

Microsoft has a well done and detailed document that describes how to set up http.sys hosting:

I haven't had a chance to try this in production, but if you are running on Windows this might be a cleaner and more easily configurable way to run ASP.NET Core applications than doing the Kestrel->IIS dance. Doing some quick over the finger performance tests with WebSurge show that running with raw Httpsys is a bit faster than running IIS->Kestrel.

For a public facing Web site you're probably better off with full IIS, but for raw APIs or internal applications Httpsys is a great option for Windows hosted server applications.


In ASP.NET Core 2.0 Microsoft is rolling out RazorPages. RazorPages is something completely new, although it's based on concepts that should be familiar to anybody who's used either ASP.NET WebPages or - gasp - WebForms (minus Viewstate).

When I first heard about RazorPages a while back I had mixed feelings about the concept. While I think that script based framework is an absolute requirement for many Web sites that deal primarily with content, I also felt like requiring a full ASP.NET Core application setup that requires a full deployment process just to run script pages is a bit of an oxymoron. After all one of the advantages of tools like WebPages and WebForms is that you don't have to 'install' an application and you just drop a new page into a server and run.

RazorPages are different - they depend on ASP.NET Core and they are an intrinsic part of the ASP.NET Core MVC platform. Razorpages use the same concepts and share the same Razor components as MVC Views and controllers, so for all intents and purposes RazorPages is a different repackaging for MVC.

So why use it? Think about how much clutter there is involved in MVC to get a single view fired up in the default configuration ASP.NET MVC projects use:

  • Controller Class with a Controller Method (Controllers folder)
  • View Model (Models Folder)
  • View (View/Controller folder)

IOW, code in MVC is scattered all over the place. Some of this can be mitigated with Feature folders where all related files are stored in a single folder, but you still essentially have view html, view model code and controller code scattered across 3 different files.

RazorPages provides much of the same functionality in a much simpler package. In fact, with Razor Pages you can create single pages that include both HTML, Model and Controller code:

@model IndexModel
@using Microsoft.AspNetCore.Mvc.RazorPages
@functions {

    public class IndexModel : PageModel
        public string Name { get; set; }

        public string Message { get; set; }

        public void OnGet()
            Message = "Getting somewhere";

        public void OnPost()

            if (this.ModelState.IsValid)
                Message = "Posted all clear!";
                Message = "Posted no trespassing!";
    Layout = null;
<!DOCTYPE html>
<form method="post" asp-antiforgery="true">
    <input asp-for="Name" type="text" />
    <button type="submit" class="btn btn-default">Show Hello</button>

<div class="alert alert-warning">

Although I really like the fact that you can embed a model right into the Razor page as shown for simple pages, this gets messy quickly. More commonly you pull out the PageModel into a separate class, and the default template that creates a RazorPage in Visual Studio does just that. When you create a new RazorPage in Visual studio you get a .cshtml and a nested .cshtml.cs files:

Figure 7 - RazorPage Code Behind uses a hybrid View/Controller class

RazorPages Runtime Compilation

Before you completely dismiss inline code in the .cshtml template, consider that code inside the RazorPage is dynamically compiled at runtime, which means you can make changes to the page without having to recompile and restart your entire application!

The PageModel subclass in this scenario becomes a hybrid of controller and model code very similar to the way many client side frameworks like Angular handle the MV* operation which is more compact and easier to manage than having an explicit controller located in yet another external class.

PageModel supports implementation of a few well known methods like OnGet(), OnPost() etc for each of the supported verbs that can handle HTTP operations just like you would in a controller. An odd feature called PageHandlers using the asp-page-handler="First" attribute lets you even further customize the methods that are fired with a method postfix like OnPostFirst(), so that you can handle multiple forms on a single page for example.

While traditional MVC feels comfortable and familiar, I think RazorPages offers a clean alternative with page based separation of concerns in many scenarios. Keeping View and View specific Controller code closely associated usually makes for an easier development workflow and I'd already moved in this direction with feature folder setup in full MVC anyway. If you squint a little, the main change is that there are no more explicit multi-concern controllers, just smaller context specific classes.

RazorPages is not going to be for everyone, but if you're like me and initially skeptical I would encourage you to check them out. It's a worthwhile development flow to explore and for the few things I still use server generated HTML for I think RazorPages will be my tool of choice on ASP.NET Core.

The Voices of Reason


October 24, 2017

# re: .NET Core 2.0 and ASP.NET 2.0 Core are Here

Thank you for the very nice post.

I am still considering should I start with .NET CORE and you provided just what I needed.

Matt Warren
October 24, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Dotnet run builds the project again and that's seems to be where the issue is as .NET goes through re-packaging the application in order to run it.

Scott Hanselman wrote up a post on this, see SpeedOfDotnetRunVsTheSpeedOfDotnetForPublishedAppsPlusSelfcontainedNETCoreApps, it might have some tips/pointers on how to speed it up

October 24, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Re: build slowness

If you change your build output logging to verbose it shows the reason why it's building each project.

I found I had some resources that were set to Copy Always which meant the build would always be triggered regardless if anything had changed. Changing them to Copy If Newer meant the build was no longer being executed if nothing had changed 😃

October 24, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

One of the nicest features of 2.0 (actually introduced in 1.6) is the new SDK style .csproj Project format.

Actually, it was introduced in .Net Core SDK 1.0. (It's why .Net Core SDK was in preview for a while even after .Net Core 1.0 was released.) No .Net Core-related product I know of ever had a 1.6 version (except for .Net Standard, but that's not really a product).

October 24, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Nice article. Covering ASP Pages is something I've heard about, but now I'm going to rewrite a small web app in ASP Pages, instead of having lots of Controllers that all they do is return one or two views.

Chuck Conway
October 25, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Thanks for the summary of .Net Core 2.0. I have also used .Net Core for a new project. I find the slowness you've talked about maddening. I've tried both Jetbrains Rider on a Mac and a Windows, with similar results. I encounter the slowness on initial startup and while building. My hunch is it's related to Nugut. There is an option to turn Nuget off. I haven't tried this option to see if it is truly the case. In Rider (the latest version, 2017.2.x), I've encountered an issue where the project types of netcoreapp2.0 vs netstandard2.0's versions are switched. So .netstandard is on version 2.0.1, and netcoreapp2.0 is still on 2.0.0, but for some reason Rider switches the versions. Both Visual Studio and Rider grind to a halt. Taking forever to finally display a message along the lines of "Unable to find version 2.0.1 for netcoreapp2". Like you mentioned, I hope they get the tooling dialed in soon. It's a major pain point in working with .Net Core 2.0.

October 26, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Excellent writeup, Rick. I've been hesitant to work in .NET Core for my games and side projects for all the same reasons you have mentioned. We've been using it where I work, but it felt forced -- way premature and we were constantly spinning our wheels with issues when they changed directions unexpectedly. I was put on forking a 4.6 project and hooking it up to the "new stuff" in the Core world. When I tried to pull in anything from the new world targetting the 1.6< Standard, it tried to pull in pretty much everything from .NET Core. Luckily 4.7.1 was just released and once we updated our Standard stuff to 2.0 (last week), everything clicked (really, like night and day). It's beautiful. I can reference their stuff without pulling down dozens of .NET Core/Standard dependencies. I am starting to feel the same confidence in .NET Core that I started to feel in .NET 2.0 when we got (among other things) proper generics. It's maturing before our eyes and becoming truly attractive as a platform to build professional-grade products and services on. Looking forward to the future!

Alexander Batishchev
October 29, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

My (and many's) rule of thumb: wait for version 3.0 of any Microsoft product. Even I work for Microsoft myself, still.

October 31, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Thanks for the detailed post, Rick. You've given me the confidence to start looking at migrating some projects to .NET Core.

Freak Power
December 11, 2017

# re: .NET Core 2.0 and ASP.NET Core 2.0 are Here

Microsoft lost me with all this. .NET is a mess right now, and .NET Core makes no sense at all because MS. didn't clearly explain what is really for.


West Wind  © Rick Strahl, West Wind Technologies, 2005 - 2018