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


Many of us have been patiently waiting through the long and windy road that has been the inception of the .NET Core and ASP.NET Core platforms. After a very rocky 1.0 and set of 1.x releases, version 2.0 of the new .NET frameworks and tooling have finally arrived a few weeks back. You know the saying: "Don't use any 1.x product from Microsoft", and this is probably more true than ever with .NET Core and ASP.NET Core. The initial releases, while technically functional and powerful, were largely under-featured and sported very inconsistent and ever changing tooling. Using the 1.x (and pre-release) versions involved quite a bit of pain and struggle just to keep up with all the developments along the way.

.NET Core 2.0 and ASP.NET Core 2.0 - The Promised Land?

Version 2.0 of .NET Standard, .NET Core and ASP.NET Core improve the situation considerably by significantly refactoring the core feature set of .NET Core, without compromising all of the major improvements that the new framework has brought in Version 1.

The brunt of the changes involve bringing back APIs that existed in full framework to make .NET Core 2.0 and .NET Standard 2.0 more backwards compatible with full framework. It's now vastly easier to move existing full framework code to .NET Core/Standard 2.0. It's hard to understate how important that step is, as 1.x simply in many ways felt hobbled by missing API and sub-features that made it difficult to move existing code and libraries to .NET Core/Standard. Bringing the API breadth back to close compatibility resets the expectation of what amounts to a functional set of .NET features that most of us have come to expect of .NET.

These subtle, but significant improvements make the overall experience of .NET Core and ASP.NET Core much more approachable especially when coming from previous versions of .NET. More importantly it should also make it much easier for third party providers to move their components to .NET Core so that the support eco-system for .NET Core applications doesn't feel like a backwater as it did during the 1.x days.

These changes are as welcome as they were necessary and in my experience with the 2.0 wave of tools has been very positive. I've been able to move two of my most popular libraries to .NET Core 2.0 with relatively little effort - something that would have been unthinkable with the 1.x versions. The overall feature breadth is pretty close to full framework, minus the obvious Windows specific feature set.

ASP.NET Core 2.0 also has many welcome improvements including a simplified configuration setup that provides sensible defaults, so you don't have to write the same obvious startup code over and over. There are also many new small enhancements as well as a major new of RazorPages which bring controller-less Razor pages to ASP.NET Core.

Overall 2.0 is a massive upgrade in functionality, that brings back features that realistically should have been there from the start.

But it's not all unicorns and rainbows - there are still many issues that need to be addressed moving forward. First and foremost is that the new SDK style project tooling leaves a lot to be desired with slow compilation, slow test tooling, and buggy tool support for multi-targeted projects in Visual Studio. Visual Studio in general seems to have taken a big step back in stability in recent updates when it comes to .NET Core projects.

The Good outweighs the Bad

Overall the improvements in this 2.0 release vastly outweigh the relatively few - if not insignificant - problems, that still need to be addressed. The outstanding issues are well known and on the board for fixes in the hopefully not so distant future. Most of these relate to tooling and tooling performance rather than the frameworks themselves. While inconvenient, these tooling shortcomings are not what I would consider show stoppers, but mostly nuisances that are likely to be addressed soon enough.

To be clear where I stand:
.NET Core 2.0 and ASP.NET 2.0 is my demarcation line, my line in the sand, where I get my butt off my hands and finally jump in.

The 2.0 release feels like a good jumping-in point, to dig in and start building real applications with - a feeling that I never had with the 1.x releases. Sure I dabbled and worked through my samples and learned but with 1.x I never felt like I'd get through a project without getting stuck on some missing piece of kit.

For me 2.0 strikes the right balance of new features, performance and platform options that I actually want to use, without giving up many of the conveniences that earlier versions of .NET offered. The 2.0 features no longer feel like a compromise between the old and new but a way forward to new features and functionality that is actually useful and easy to work with in ways that you would expect to on the .NET platform. Plus many of the hot shiny new features in ASP.NET Core in particular. There's lots to like and always has been in ASP.NET Core.

Let's take a look at some of the most important details of what's changed.

What is .NET Standard?

Not sure what .NET Standard is and it relates to .NET Core and other .NET frameworks? Check out my previous blog post that explains what .NET Standard is, why it's a useful new concept and how you can use it:

2.0 Versions of .NET Core and .NET Standard bring back many .NET APIs

The first and probably most significant improvement in the 2.0 releases is that .NET Standard and .NET Core 2.0 bring back many of the APIs we've been using since the beginnings of .NET in the full framework, that were not supported initially by .NET Core 1.x.

When .NET Core 1.x came out they were largely touted as trimmed down, high performance versions of the full .NET Framework. As part of that effort there was a lot of focus on trimming the fat and providing only core APIs as part of .NET Core and .NET Standard. The bulk of the .NET Base Class Library was also broken up into a huge number of small hyper-focused packages.

All this resulted in a much smaller framework, but unfortunately also brought a few problems:

  • Major incompatibilities with classic .NET framework code (hard to port code)
  • Huge clutter of NuGet Packages in projects
  • Many usability issues trying to perform common tasks
  • A lot of mental overhead trying to combine all the right pieces into a whole

With .NET Core 1.0 many common NET Framework APIs were either not available or buried under different API interfaces that often were missing critical functionality. Not only was it hard to find stuff that was under previously well known APIs, but a lot of functionality that was taken for granted (Reflection, Data APIs, XML for example) was refactored down to near un-usability.

Bringing back many Full Framework Features

.NET Core 2.0 - and more importantly .NET Standard 2.0 - add back a ton of functionality that was previously cut from .NET Core/Standard, bringing back a large swath of functionality that existed in full framework .NET. In 1.x it was really difficult to port existing code. The feature footprint with .NET Core 2.0 is drastically improved (~150% of APIs added) and compatibility with existing full framework functionality is preserved for a much larger percentage of code.

In real terms this means that it's much easier now to port existing full framework code to .NET Standard or .NET Core and have it run with no or only minor changes.

Case in point: I took one of my 15 year old general purpose libraries - Westwind.Utilities which contains a boat load of varied utility classes that touch a wide variety of .NET features - and I was able to re-target the library to .NET Standard as well as .NET 4.5. More than 95% of the library could migrate without changes and only a few small features needed some tweaks (encryption, database) and a few features had to be cut out (low level AppDomain management and Drawing features). Given that this library was such an unusual hodgepodge of features, more single-focused libaries will fare even better in terms of conversions. If you're not using a few of the APIs that have been cut or only minimally implemented, chances are porting to .NET Standard will require few or even no changes.

You can read more about what was involved in this process my Multi-Targeting and Porting a .NET Library to .NET Core 2.0 post.

Runtimes are Back

One of the key bullet points Microsoft touted with .NET Core is that you can run side by side installations of .NET Core. You can build an application and ship all the runtime files and dependencies in a local folder - including all the .NET dependencies as part of your application. The benefit of this is that you can much more easily run applications that require different versions of .NET on the same machine. No more trying to sync up and potentially break applications due to global framework updates. Yay! Right?

.NET Core 1.x - Fragmentation & Deployment Size

Well - you win some, you lose some. With Version 1.x of .NET Core and ASP.Core the side effect was that the .NET Core and ASP.NET frameworks were fragmented into a boatload of tiny, very focused NuGet packages that had to be referenced explicitly in every project. These focused packages are a boon to the framework developers as they allow for nice feature isolation and testing, and the ability to rev versions independently for each component.

But the result of all this micro-refactoring was that you had to add every tiny little micro-refactored NuGet Package/Assembly explicitly to each project. Finding the right packages to include was a big usability problem for application and library developers.

Additionally when you published Web projects all those framework files - plus all runtime runtime dependencies had to be copied to the server with 1.x, making for a huge payload to send up to a server for publishing even for a small HelloWorld application.

Meta Packages in 2.0

In .NET Core 2.0 and ASP.NET 2.0 this is addressed with system wide Framework Meta Packages that can be referenced by an application. These packages are installed using either the SDK install or a 'runtime' installer and can then be referenced from within a project as a single package. So when you reference .NET Core App 2.0 in a project, it automatically includes a reference to the .NET Core App 2.0 meta package. Likewise if you have a class library project that references .NET Standard - that meta package with all the required .NET Framework libraries is automatically referenced. You don't need to add any of the micro-dependencies to your project. The runtimes reference everything in the runtime, so in your code you only need to apply namespaces, but no extra packages.

There's also an ASP.NET Core meta package that provides all of the ASP.NET Core framework references. Each of these meta packages have a very large predefined set of packages that are automatically referenced and available to your project's code.

In your projects this means you can reference .NET Standard in a class library project and get references to all the APIs that are part of .NET Standard with NetStandard.Library reference in the screenshot below. In applications, you can reference Microsoft.NETCoreApp which is essentially a reference to .NET Core 2.0 - here you specifying a very specific instance of runtime for the application. For ASP.NET the Microsoft.AspNetCore.All package brings in all ASP.NET and EntityFramework related references in one simple reference.

Here's an example of a two project solution that has an ASP.NET Core Web app and a .NET Standard targeted business logic project:

Figure 1: Package references are manageable again in V2.0

Notice that the project references look very clean overall - I only explicitly add references to third party NuGet packages - all the system refs comes in automatically via the single meta package. This is even less cluttered than a full framework project which still needed some high level references. Here everything is automatically available for referencing.

This also is nice for tooling that needs to find references (Ctrl-. in VS or Alt-Enter for R#). Because everything is essentially referenced, Visual Studio or Omnisharp can easily find references and namespaces and inject them into your code as using statements. Nice.

Runtimes++

In a way these meta packages feel like classic .NET runtime installs and in an indirect way they are. Microsoft now provides .NET Core and ASP.NET Core runtimes that are installed from the .NET Download site and can either be installed via the plain runtime or the .NET SDK that includes all the compilers and command line tools so you can build and manage a project.

You can install multiple runtimes side by side and they are available to many applications to share. This means the same packages don't have to be installed over and over for each and every application, which makes deployments a heck of a lot leaner than in 1.x applications.

You can still fall back to local packages installed with in the application's output folder and override global installed packages, so now you get the best of all worlds:

  • Run an application with a pre-installed Runtime (default)
  • Run an application with a pre-installed Runtime and override packages locally
  • Run an application entirely with locally installed packages

In short you now get to have your cake and eat it too, as you get to choose exactly where your runtime files are coming to.

Publishing Applications

In most cases with 2.0 publishing an application to a Web server is much leaner than in prior versions. Your publish folder and what needs to get sent to the server amounts to just your code plus any explicit third party dependencies you added to the project. You are no longer publishing runtime files to the server.

Here's the publish folder of the Solution shown above:

Figure 2: Published output contains just your code and your explicit dependencies

This means publishing your application is much more lightweight - after the initial runtime installation. It's still possible to deploy full runtimes just as you could in 1.x releases, it just no longer the default and you have to explicitly specify the runtime to publish.

.NET SDK Projects

One of the nicest features of 2.0 (actually initially introduced in 1.1) is the new SDK style .csproj Project format. This project format is very lean and easily readable - quite in contrast to the verbose and cryptic older .csproj format.

For example, it's not hard to glean what's going on in this .csproj project file:

<Project Sdk="Microsoft.NET.Sdk.Web">
  <PropertyGroup>
    <TargetFramework>netcoreapp2.0</TargetFramework>      
  </PropertyGroup>

	<ItemGroup>
		<PackageReference Include="Microsoft.AspNetCore.All" Version="2.0.0" />
		<PackageReference Include="Serilog.Extensions.Logging" Version="2.0.2" />
		<PackageReference Include="Serilog.Sinks.RollingFile" Version="3.3.0" />		
	</ItemGroup>
	<ItemGroup>
	  <ProjectReference Include="..\AlbumViewerBusiness\AlbumViewerBusiness.csproj" />
	</ItemGroup>

  <ItemGroup>
    <Content Update="wwwroot\**\*;Areas\**\Views;appsettings.json;albums.js;web.config">
      <CopyToPublishDirectory>PreserveNewest</CopyToPublishDirectory>
    </Content>    
  </ItemGroup>
  <ItemGroup>
    <Compile Remove="logs\**" />
    <Content Remove="logs\**" />
    <EmbeddedResource Remove="logs\**" />
    <None Remove="logs\**" />
  </ItemGroup>

  <ItemGroup>
    <DotNetCliToolReference Include="Microsoft.DotNet.Watcher.Tools" Version="2.0.0" />
  </ItemGroup>

  <ItemGroup>
    <None Update="albums.js">
      <CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
    </None>
  </ItemGroup>
</Project>  

Notice that file references are all but gone in the project file - projects now assume all files are included except those you explicitly exclude which drastically reduces the file references in a project. The other benefit here is that you can simply drop files in a folder to become part of a project - you no longer have to add files to a project explicitly.

Compared to the morass that was the old .csproj format this is very clean and lean.

Additionally the new project format supports multi-targeting to multiple .NET framework versions. I've talked a few times about porting existing libraries to .NET Standard, and using the new project format it's quite easy to set up a library to target both .NET 4.5 and .NET Standard for example.

Here's an example of my Westwind.Utilities library that does just that:

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <TargetFrameworks>netstandard2.0;net45;net40</TargetFrameworks>
    <RuntimeIdentifiers>win7-x86;win7-x64</RuntimeIdentifiers>
    <Authors>Rick Strahl</Authors>
     <Version>3.0.2</Version>
    <AssemblyVersion>3.0.2.0</AssemblyVersion>
    <FileVersion>3.0.2.0</FileVersion>  
    <PackageId>Westwind.Utilities</PackageId>
    <RootNamespace>Westwind.Utilities</RootNamespace>
    
    ...Nuget info block omitted
    
  </PropertyGroup>

  <PropertyGroup Condition="'$(Configuration)'=='Debug'">
    <DefineConstants>TRACE;DEBUG;</DefineConstants>
  </PropertyGroup>

  <PropertyGroup Condition=" '$(Configuration)' == 'Release' ">
    <NoWarn>$(NoWarn);CS1591;CS1572;CS1573</NoWarn>
    <GenerateDocumentationFile>true</GenerateDocumentationFile>
    <IncludeSymbols>true</IncludeSymbols>
    <DefineConstants>RELEASE</DefineConstants>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="Newtonsoft.Json" Version="10.0.3" />
  </ItemGroup>

  <ItemGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
    <PackageReference Include="System.Data.SqlClient" Version="4.4.0" />
  </ItemGroup>
  <PropertyGroup Condition=" '$(TargetFramework)' == 'netstandard2.0'">
    <DefineConstants>NETCORE;NETSTANDARD;NETSTANDARD2_0</DefineConstants>
  </PropertyGroup>

  
  <ItemGroup Condition=" '$(TargetFramework)' == 'net45' ">
    <Reference Include="mscorlib" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Security" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Configuration" />
  </ItemGroup>
  <PropertyGroup Condition=" '$(TargetFramework)' == 'net45'">
    <DefineConstants>NET45;NETFULL</DefineConstants>
  </PropertyGroup>

  
  <ItemGroup Condition=" '$(TargetFramework)' == 'net40' ">
    <Reference Include="mscorlib" />
    <Reference Include="System" />
    <Reference Include="System.Core" />
    <Reference Include="Microsoft.CSharp" />
    <Reference Include="System.Data" />
    <Reference Include="System.Web" />
    <Reference Include="System.Drawing" />
    <Reference Include="System.Security" />
    <Reference Include="System.Xml" />
    <Reference Include="System.Configuration" />
  </ItemGroup>

  <PropertyGroup Condition=" '$(TargetFramework)' == 'net40'">
    <DefineConstants>NET40;NETFULL</DefineConstants>
  </PropertyGroup>

</Project>

The project defines three framework targets:

<TargetFrameworks>netstandard2.0;net45;net40</TargetFrameworks>

and then uses conditional target framework filtering to add dependencies. Visual Studio can visualize these dependencies for each target as well:

Figure 3 - Multiple targets displayed in Visual Studio

Visual Studio 2017.3+ also has a new Target drop down that lets you select which target is currently used to display code and errors in the environment:

Figure 4 - Active target compiler constants are evaluated in the code editor so code excluded for a given target is low-lighted.

There are other features in Visual Studio that makes it target aware:

  • Intellisense shows warnings for APIs that don't exist on any given platform
  • Compiler errors now show the target platform for which the error applies
  • Tests using the Test Runner respect the active target platform (VS2017.4)

When you compile this project, the build system automatically builds output for all three targets which is very nice if you've ever tried to create multi-target projects with the old project system (hint: it sucked!).

It can also create a NuGet Package that wraps up all targets into the pacakge. If you look back at the project file you'll note that the NuGet Properties are now stored as part of the .csproj file.

Here's what the build output from my 3 target project looks like:

Figure 5 - Multi-target projects automagically build for all target platforms and can create a NuGet package.

This, friends, is pretty awesome to me and frankly something that should have been done a long time ago in .NET!


The Voices of Reason


 

Dorde
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


Ade
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 😃


svick
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).


anon1234
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.


Vitoc
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.


Marc
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