Tag Archives: .NET Core

AWS Lambda .NET Core 2.1 Support Released

Amazon Web Services (AWS) has just announced that its serverless function offering, AWS Lambda, now supports the .NET Core 2.1 runtime, which was released towards the end of May 2018.

Quoting the official announcement:

“Today we released support for the new .NET Core 2.1.0 runtime in AWS Lambda. You can now take advantage of this version’s more performant HTTP client. This is particularly important when integrating with other AWS services from your AWS Lambda function. You can also start using highly anticipated new language features such as Span<T> and Memory<T>.

“We encourage you to update your .NET Core 2.0 AWS Lambda functions to use .NET Core 2.1 as soon as possible. Microsoft is expected to provide long-term support (LTS) for .NET Core 2.1 starting later this summer, and will continue that support for three years. Microsoft will end its support for .NET Core 2.0 at the beginning of October, 2018[2]. At that time, .NET Core 2.0 AWS Lambda functions will be subject to deprecation per the AWS Lambda Runtime Support Policy. After three months, you will no longer be able to create AWS Lambda functions using .NET Core 2.0, although you will be able to update existing functions. After six months, update functionality will also be disabled.

“[1] See Microsoft Support for .NET Core for the latest details on Microsoft’s .NET Core support.
“[2] See this blog post from Microsoft about .NET Core 2.0’s end of life.”

The choice here seems obvious: upgrade and get faster HttpClient, new language features, and long-term support; or lose support for your functions targeting .NET Core 2.0 (whatever that actually means).

In order to migrate to .NET Core 2.1, you’ll need the latest tooling – either version 1.14.4.0 of the AWS Toolkit for Visual Studio, or version 2.2.0 of the Amazon.Lambda.Tools NuGet package.

Check out the official announcement at the AWS blog for more information, including additional tips on upgrading.

Accessing an ASP .NET Core Web Application Remotely

After setting up an empty ASP .NET Core Web Application, it’s easy to quickly run it and see something working, in the form of the usual “Hello World”:

When trying to deploy this somewhere though, you might be disappointed to notice that you can’t access the web application from another machine:

In fact, you’ll notice that you can’t even access it from the same machine if you use the actual hostname rather than localhost.

This is because by default, Kestrel will listen only on localhost. In order for another machine to access the web application using the server’s hostname, the web application must specify the endpoints on which Kestrel will listen to, using code or command-line arguments.

Note: you may also need to open a port in your firewall.

In code, this can be done by invoking UseUrls() in the webhost builder as follows:

        public static IWebHost BuildWebHost(string[] args) =>
            WebHost.CreateDefaultBuilder(args)
                .UseStartup<Startup>()
                .UseUrls("http://myhostname:54691")
                .Build();

Replace “myhostname” with the hostname of the server, and note that the localhost endpoint will still work even though it’s not specified explicitly here.

If you want to pass the the endpoint(s) via command line parameters instead, you can do so via the --urls argument. First, you need to change the BuildWebHost() method generated by the project template as per this GitHub comment, to allow command line parameters to be passed to the WebHostBuilder via configuration:

public static IWebHost BuildWebHost(string[] args)
{
    var configuration = new ConfigurationBuilder().AddCommandLine(args).Build();

    return WebHost.CreateDefaultBuilder(args)
        .UseConfiguration(configuration)
        .UseStartup<Startup>()
        .Build();
}

Then, use the --urls argument when invoking dotnet run:

dotnet run --urls http://banshee:54691/

Either of these methods is fine to allow remote machines to access your ASP .NET Core web application.

.NET Core 3 to Support Desktop Applications… Kind of

A few days ago, Microsoft published a blog post titled “.NET Core 3 and Support for Windows Desktop Applications“. Just by reading the title, I’m pretty sure many of us jumped in their seats as thoughts like “WPF on Linux” became a source of excitement.

Image source: .NET Core 3 and Support for Windows Desktop Applications

Unfortunately however, the excitement turns into a disappointed “Oh. [Awkward silence] OK.” when reading that although .NET Core 3 is planned to support desktop applications built on technologies like Windows Forms and WPF, this support is for Windows only:

“Support for Windows desktop will be added as a set of “Windows Desktop Packs”, which will only work on Windows. .NET Core isn’t changing architecturally with this new version. We’ll continue to offer a great cross-platform product, focused on the cloud. We have lots of improvements planned for those scenarios that we’ll share later.”

The article does mention that this will bring several benefits ranging from performance improvements to deployment options, but this pales in comparison to the prospect of going cross-platform.

But given that they “are planning on releasing a first preview of .NET Core 3 later this year and the final version in 2019”, I can only wonder why they would spend a year doing a huge amount of work that most people won’t even care about, and pass it as a major release of .NET Core.

Time will tell, but one can get an idea of how people feel about this from the comments in the blog post.

My guess is that this could be part of a long-term strategy to retire the full .NET Framework, rather than bringing any real value to .NET Core or desktop applications.

Reading RabbitMQ Settings Using .NET Core Configuration

The .NET Core Configuration system is extremely powerful and flexible. One of the features that I use the most is its capability to bind structured settings from a source (e.g. JSON file) to a C# object.

A very good example of this is obtaining RabbitMQ settings so that you can populate the ConnectionFactory. In the past, I’ve created a DTO (class) for this, and a parser that could populate this class based on a connection string format that I invented on the spot out of necessity. The good news is that you don’t have to do this any more. .NET Core configuration allows you to bind your config to an object, even if it’s coming from a third party library. Let’s see how.

Typical RabbitMQ Configuration

First, in order to use RabbitMQ, we need to install the RabbitMQ Client NuGet package.

Install-Package RabbitMQ.Client

Next, we’ll typically create a connection by means of the ConnectionFactory. We’ll need to populate the necessary fields, whether directly or by reading them from config. Technically, most of the settings below are not necessary because defaults are assumed if not provided, but we’ll include them anyway as we’re not assuming everyone is connecting to RabbitMQ on localhost.

            var connectionFactory = new ConnectionFactory()
            {
                HostName = "localhost",
                UserName = "guest",
                Password = "guest",
                VirtualHost = "/",
                AutomaticRecoveryEnabled = true,
                RequestedHeartbeat = 30
            };

If we use .NET Core configuration, we don’t even need to do this any more.

Connection Settings From JSON File

Let’s start by adding a new text file to the project called appsettings.json. From its properties, change it to copy to the output directory on build (Copy always and Copy if newer are both fine). In the file, we’ll add the JSON equivalent of what we have in ConnectionFactory above:

{
  "RabbitMqConnection": {
    "HostName": "localhost",
    "Username": "guest",
    "Password": "guest",
    "VirtualHost": "/",
    "AutomaticRecoveryEnabled": true,
    "RequestedHeartbeat": 30
  }
}

Now, we need a way to read this JSON file and bind it to a ConnectionFactory object. To do that, we need the following NuGet packages:

Install-Package Microsoft.Extensions.Configuration
Install-Package Microsoft.Extensions.Configuration.Json
Install-Package Microsoft.Extensions.Configuration.Binder

The .NET Core configuration system is split into multiple packages, so you can bring in only what you actually need. The first package is the heart of the framework, and you don’t need to install it directly because the second and third packages will both bring it in as a dependency when you install them. As for the Json and Binder packages, we’ll see what they do in a minute.

.NET Core configuration is loaded by means of a ConfigurationBuilder object. In our case, we’ll have:

            var config = new ConfigurationBuilder()
                .AddJsonFile("appsettings.json")
                .Build();

The AddJsonFile() extension method is provided by the Json package (the second one we installed earlier). The result of this is an object which implements IConfigurationRoot, and we can use this to read our settings.

Next, we’ll prepare an empty ConnectionFactory object that the binder will populate from the configuration in the next step.

var connectionFactory = new ConnectionFactory();

Finally, we can bind the entire “RabbitMqConnection” section of the appsettings.json file to our ConnectionFactory object, using the Bind() method (provided via the Binder package we installed earlier):

config.GetSection("RabbitMqConnection").Bind(connectionFactory);

If the key-value pairs in the JSON section match properties on the connectionFactory object, they will be set. You’ll know it worked because RequestedHeartbeat has a default value of 60, but it will be overridden by the value of 30 from appsettings.json.

Testing Connectivity

Now that you are populating the ConnectionFactory, you can connect in the same way as you used to before. This should suffice, as you’ll get an exception if your connection settings are incorrect:

            using (var conn = connectionFactory.CreateConnection())
            {
                Console.WriteLine("Connected!");

                // ...                

                Console.ReadLine();
            }

But if you want to make damn sure that you can actually interact with RabbitMQ, you can write a minimal consumer, and then send it messages via the Management Plugin’s Web UI:

            using (var conn = connectionFactory.CreateConnection())
            using (var channel = conn.CreateModel())
            {
                Console.WriteLine("Connected!");

                const string queueName = "madrid";
                channel.QueueDeclare(queueName, true, false, false, null);

                var consumer = new EventingBasicConsumer(channel);
                consumer.Received += (s, a) => Console.WriteLine("Message received!");
                channel.BasicConsume(queueName, true, consumer);

                Console.ReadLine();
            }

Wondering about the name of the queue? It’s because I found lots of them in Madrid. No kidding:

Summary

The point here was to show you how you can read settings from a section of a JSON file and have it directly deserialized into an object, using the binder feature of .NET Core configuration. The example here is specific to RabbitMQ, but you can use the same approach with any class you like, as long as the properties have public setters.

Also, remember that .NET Core configuration actually targets .NET Standard. That means you can use it not only with .NET Core apps, but also in the full .NET Framework, and any other compatible runtimes.

.NET Core 2.0: Referencing .NET Framework Libraries: A Topshelf Experiment

Referencing .NET Framework Libraries in .NET Core 1.1

There are many old libraries targeting the .NET Framework which, for various reasons, do not yet target .NET Core or .NET Standard. Topshelf, a fantastic library that helps you to easily create Windows services, is one of these. At the time of writing this article, the last release of Topshelf was 4.0.3 back in October 2016. There was no way that Topshelf could target .NET Standard because the .NET Standard spec did not support the APIs that are required for it to function.

This was a problem because there was no way you could create a Windows service using Topshelf for a .NET Core 1.1 application. Simply trying:

Install-Package Topshelf

…is bound to fail miserably:

If you want to make a Windows service out of an application targeting .NET Core 1.1, then you have to use an alternative such as NSSM.

What Changed in .NET Core 2.0

With the release of .NET Core 2.0 and .NET Standard 2.0, applications or libraries targeting either of these are able to reference old libraries targeting the full .NET Framework. Presumably this is because the .NET Core/Standard 2.0 implementations have enough API coverage to overlap with what the full framework was able to offer.

Quoting the .NET Core/Standard 2.0 announcement linked above:

“You can now reference .NET Framework libraries from .NET Standard libraries using Visual Studio 2017 15.3. This feature helps you migrate .NET Framework code to .NET Standard or .NET Core over time (start with binaries and then move to source). It is also useful in the case that the source code is no longer accessible or is lost for a .NET Framework library, enabling it to be still be used in new scenarios.

“We expect that this feature will be used most commonly from .NET Standard libraries. It also works for .NET Core apps and libraries. They can depend on .NET Framework libraries, too.

“The supported scenario is referencing a .NET Framework library that happens to only use types within the .NET Standard API set. Also, it is only supported for libraries that target .NET Framework 4.6.1 or earlier (even .NET Framework 1.0 is fine). If the .NET Framework library you reference relies on WPF, the library will not work (or at least not in all cases). You can use libraries that depend on additional APIs,but not for the codepaths you use. In that case, you will need to invest singificantly in testing.”

Example with Topshelf

In order to actually test this out, you’ll need to have Visual Studio 15.3 or later. You will also need to separately install the .NET Core 2.0 SDK.

In an earlier section, we tried installing Topshelf in a .NET Core 1.1 application, and failed. Let’s try doing the same thing with a .NET Core 2.0 application:

Install-Package Topshelf

The package installation works pretty well:

However, the warning that shows under the dependency is not very promising:

There’s only one way to find out whether this will actually work in practice.

Let’s steal the code from the Topshelf quickstart documentation:

public class TownCrier
{
    readonly Timer _timer;
    public TownCrier()
    {
        _timer = new Timer(1000) {AutoReset = true};
        _timer.Elapsed += (sender, eventArgs) => Console.WriteLine("It is {0} and all is well", DateTime.Now);
    }
    public void Start() { _timer.Start(); }
    public void Stop() { _timer.Stop(); }
}

public class Program
{
    public static void Main()
    {
        HostFactory.Run(x =>                                 //1
        {
            x.Service<TownCrier>(s =>                        //2
            {
               s.ConstructUsing(name=> new TownCrier());     //3
               s.WhenStarted(tc => tc.Start());              //4
               s.WhenStopped(tc => tc.Stop());               //5
            });
            x.RunAsLocalSystem();                            //6

            x.SetDescription("Sample Topshelf Host");        //7
            x.SetDisplayName("Stuff");                       //8
            x.SetServiceName("Stuff");                       //9
        });                                                  //10
    }
}

Nope, looks like Topshelf won’t work even now.

I guess the APIs supported by .NET Core 2.0 still do not have enough functionality for Topshelf work as-is. Other .NET Framework libraries may work though, depending on the dependencies they require. In the “.NET Core 2.0 Released!” video, one of the demos shows SharpZipLib 0.86 (last released in 2011) being installed in an ASP .NET Core 2.0 application. It is shown to build, but we don’t get to see whether it works at runtime.

It is still early, and I suppose we have yet to learn more about the full extent of support for .NET Framework libraries from .NET Core 2.0 applications and .NET Standard 2.0 libraries. The problem is that when evaluating a third-party library such as Topshelf, it’s difficult to determine whether its own dependencies fall within the .NET Standard API set. This looks to me like a matter of pure trial and error.

.NET Core Tools Telemetry

Microsoft may be winning the hearts of developers with their open-sourcey behaviour, but their attitude towards privacy hasn’t changed at all. As if Windows 10 sending usage data to Microsoft wasn’t enough, the .NET Core toolchain does it too.

It’s called Telemetry, and the .NET Core documentation explains the extent of the data that is sent to Microsoft whenever you run a dotnet subcommand.

The problem with .NET Core’s telemetry is not so much the nature of the data that is collected, but the fact that it is done by default, and you have to opt out if you don’t want it. That’s exactly the opposite of how it should be, with many people citing problems of privacy, security, and corporate buy-in in Issue #3093 on GitHub.

The fact that the issue is still open after nearly a year shows that the quest to “improve your experience” (whatever that means) and “provide a great product” is a lot more important than your privacy.

To disable telemetry, you have to add an environment variable called DOTNET_CLI_TELEMETRY_OPTOUT and give it a value of 1. (There are other valid values, such as “true”.) If you follow the instructions and set it in cmd.exe (Windows) or export it in a Terminal window (Linux), as the official documentation suggests, that setting is only valid for the active command line window! Instead, follow the instructions below to set the environment variable permanently.

Opting Out Under Windows

Under Windows, go to Advanced System Settings (via Control Panel or directly from the Start menu) and add it to the environment variables from there.

Opting Out Under Linux

Under Linux, you can permanently set environment variables either by editing the script file for the shell you’re using (e.g. .bashrc for Bash), or else adding an entry in /etc/environment which will apply to all shells as well as non-shell windows:

echo "DOTNET_CLI_TELEMETRY_OPTOUT=1" | sudo tee -a /etc/environment

You will need to restart the system for this change to take effect.

Opting Out Under Mac

I don’t have a Mac, so all I can tell you is to get a decent OS. 😉

Seriously though, the same instructions for Linux should presumably work on a Mac.

Setting up .NET Core on Linux

One of the biggest promises of .NET Core is the long-awaited promise of true cross-platform development. In this article, we’ll see how we can set up .NET Core on some flavours of Linux, and ensure that it works by running a simple console application.

Introduction

In general, if you want to run .NET Core on Linux, you should do the following before even starting development, to make sure it actually works:

  1. Install .NET Core itself.
  2. Create a simple .NET project.
  3. Build and run the application.

The steps to install .NET Core vary depending on the distribution you are using. Different distributions use different package managers (e.g. APT, RPM, YUM, DNF, etc) so you will often need to either add a .NET package source to your package manager’s configuration, or download binaries for .NET Core from Microsoft, before you can proceed to actually install .NET Core.

Microsoft’s Getting Started with .NET Core documentation lists a handful of supported Linux distributions, each with their own installation instructions. Unfortunately, this is not yet updated with the latest versions of several popular distributions. In fact, I have not been able to set up .NET Core in Ubuntu 17.04 (Zesty Zapus), Fedora 25, or CentOS 7. So in this article, we’ll focus on Ubuntu 16.10 (Yakkety Yak) and Linux Mint 18.1.

Unfortunately, these two are both Debian flavours, and both use the Ubuntu package server, so there is not much in the way of variety here.  In any case, let’s proceed with the setup.

Installing .NET Core on Linux Ubuntu 16.10 (Yakkety Yak)

First, we need to follow the installation instructions in the documentation in order to add the .NET package source to APT’s package source configuration:

sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ yakkety main" > /etc/apt/sources.list.d/dotnetdev.list'
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893
sudo apt-get update

Here’s what the output of most of this should look like:

With that done, we can install the .NET Core SDK:

sudo apt-get install dotnet-dev-1.0.1

Once the installation is complete, we can create and run a simple project. We can do this without writing any code ourselves, because the dotnet command provides means of generating project templates out of the box.

First, let’s create a directory for our application, and switch to it (note: the documentation provides an alternative way of doing this):

mkdir hello
cd hello

Then, we can create a simple “Hello World” console application in the current directory by running the following command:

dotnet new console

Then, with the following commands, we restore dependencies via NuGet, build the application, and run it:

dotnet restore
dotnet run

Here’s the output, so you can see that it actually worked:

Installing .NET Core on Linux Mint 18.1

The same documentation page with the instructions to install .NET Core on Ubuntu also covers Linux Mint 17. Unfortunately, this doesn’t work for Linux Mint 18. However, you’ll notice that Ubuntu 14.04 and Linux Mint 17 share the same setup instructions. And this Stack Overflow answer shows that Ubuntu 16.04 and Linux Mint 18 also use the same setup. Thus:

sudo sh -c 'echo "deb [arch=amd64] https://apt-mo.trafficmanager.net/repos/dotnet-release/ xenial main" > /etc/apt/sources.list.d/dotnetdev.list'

sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys 417A0893

sudo apt-get update

Then, like before, we install the .NET Core SDK:

sudo apt-get install dotnet-dev-1.0.1

And then, we can actually test this out:

mkdir hello
cd hello
dotnet new console
dotnet restore
dotnet run

We get our “Hello World”, so it works!

Conclusion

We’ve seen how to set up .NET Core on the Ubuntu and Mint distributions of Linux, which are very similar. Different distributions have different setup instructions, and it would be a real pain to cover all of them. The official documentation does provide installation instructions for a handful of popular distributions, but they are slow to update documentation, and do not at this time cover the latest versions.

At least, however, this should be enough to get an idea of what it takes to set things up and run a simple application on Linux using .NET Core.

Multi-Targeting .NET Standard Class Libraries

The .NET family has grown quite a bit, and to be honest, it’s a bit of a confusing mess. As I already explained in “Migrating Dandago.Finance to .NET Core“, there are now several different types of class library you can choose from (including different kinds of portable class libraries), different ideas of cross-platform (.NET Core vs Universal Windows Platform), different frameworks, and .NET Standard.

Let’s consider the following image, which shows three different .NET-based frameworks in relation to .NET Standard:

Image credit: .NET Core, .NET Framework, Xamarin – The “WHAT and WHEN to use it”

Your applications will typically be built for one of the frameworks on top (e.g. .NET Framework). But when you create a class library, you can choose to have it target a specific framework, or .NET Standard. Just as a quick recap from “Migrating Dandago.Finance to .NET Core“, targeting a particular framework (e.g. .NET Core) will not let you use the class library on others (e.g. .NET Framework), but targeting .NET Standard keeps it compatible with all of them.

This means that class libraries that target .NET Standard have maximum compatibility, but there’s a cost: not all APIs are available for .NET Standard, and targeting .NET Standard is only compatible with certain recent versions of those frameworks. If you take a look at the compatibility chart, you’ll notice for instance that .NET Standard 1.3  supports .NET Framework 4.6 and onwards.

For the most part, this is okay. But sometimes, you may want to combine .NET Standard compatibility with specific features in a particular framework, or perhaps target .NET Standard but still support older versions of a framework because you have some legacy code you can’t upgrade yet.

Multi-Targeting

When you create a new .NET Standard Class Library, its .csproj file will look something like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFramework>netstandard1.4</TargetFramework>
  </PropertyGroup>

</Project>

It is now actually possible to target multiple frameworks, by changing the <TargetFramework> element to <TargetFrameworks> (just add an ‘s’) and putting in different target framework monikers separated by semicolons. For instance, the .csproj file for my .NET Settings Framework looks like this:

<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <TargetFrameworks>netstandard1.2;net452</TargetFrameworks>
    <!-- ... -->
  </PropertyGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'net452'">
    <Reference Include="System.Configuration" />
  </ItemGroup>

  <ItemGroup Condition="'$(TargetFramework)' == 'netstandard1.2'">
    <PackageReference Include="System.ComponentModel.TypeConverter" Version="4.3.0" />
  </ItemGroup>

  <ItemGroup>
    <PackageReference Include="Microsoft.Extensions.Configuration" Version="1.1.1" />
    <PackageReference Include="Microsoft.Extensions.Configuration.Binder" Version="1.1.1" />
  </ItemGroup>

</Project>

In this project, I’m targeting .NET Standard to provide as much compatibility as possible, but I’m also using framework-specific assets from the .NET Framework (namely System.Configuration, which is used to work with App.config/Web.config files). Thus, I am targeting both .NET Standard 1.2+ and .NET 4.5.2+. This has the effect of creating separate builds for each framework.

I have dependencies in there that are applicable for both targets (the section at the end with no conditions), but I also have specific dependencies needed for each framework. For instance, the functionality that needs System.Configuration will only be available for the build that targets the full .NET Framework, and will not be usable in the .NET Standard build. Such functionality will have to be written within preprocessor directives to prevent them from breaking the other builds:

#if NET452

using System.Configuration;

// ...

#endif

Unfortunately, as of now, you have to edit the .csproj by hand if you want to do multi-targeting, because the Visual Studio tooling hasn’t quite caught up with it yet. In fact, the project settings will only allow you to target a specific version of .NET Standard from a dropdown:

And after you’ve edited the .csproj by hand, it won’t let you change it from the GUI:

Summary

  • Targeting .NET Core lets you go cross platform, but you can’t use that functionality with other frameworks.
  • Targeting .NET Standard is compatible with any framework that supports that version of .NET Standard.
  • You can multi-target a .NET Standard library to include framework-specific functionality (guarded with preprocessor directives) while keeping the rest of the library compatible with all the relevant frameworks.

Which .NET Standard Version To Target

When I migrated Dandago.Finance to .NET Core yesterday, there was something I overlooked. I realised this when I tried to install the resulting package, targeting .NET Standard 1.6, in a new project. It worked fine in a .NET Core console application, but not in one targeting the full .NET Framework:

In fact, even referencing Dandago.Finance directly results in weird stuff going on:

The problem is immediately evident if we take a look at the compatibility grid for .NET Standard, a relevant excerpt of which at the time of writing this article is the following:

Targeting each version of .NET Standard means supporting the corresponding versions of .NET Core and .NET Framework upwards. For instance, if we target .NET Standard 1.4, then we support .NET Framework 4.6.1 and up, and .NET Core 1.0 and up.

But since Dandago.Finance was built to target .NET Standard 1.6, then .NET Framework 4.6.2 and earlier could not use it (since the first version it supports is “vNext”, whatever that means in this context).

So in practice, in order to maximise a library’s compatibility, you will want to target the lowest possible version of .NET Standard. You can do this by changing the target framework from the project settings:

In the case of Dandago.Finance, .NET Standard 1.1 provided insufficient API coverage to make it work:

Targeting .NET Standard 1.2 made Dandago.Finance compile just fine, and I verified that the resulting package installs fine for console applications targeting .NET Framework 4.5.1 and up (as per compatibility chart), and .NET Core 1.0 and up.

However, this means we have had to sacrifice support for .NET Framework 4.5. This is no big deal since .NET Framework versions 4, 4.5 and 4.5.1 have been dead for over a year now. So technically we could have targeted .NET Standard 1.3 (.NET Framework 4.6 and upwards), but it’s good to give extra backwards compatibility for legacy code where we can.

Migrating Dandago.Finance to .NET Core

Microsoft has recently been heavily investing in .NET Core, which you can think of as the next generation of the .NET Framework. There are various benefits to .NET Core, the biggest one being that it is cross-platform; thus compliant code can run on Windows, Linux and Mac (and probably others in future).

In this article, we’re going to take one of my smaller projects – Dandago.Finance – and port it to .NET Core. Dandago.Finance is ideal to demonstrate a first migration because it is very small, consisting of a main project (3 classes) and a unit test project (2 classes) – both class libraries.

Before we start, make sure you are using the latest tools (such as the recently released Visual Studio 2017). .NET Core tools have undergone a lot of radical changes (e.g. project.json is dead) so you don’t want to be learning based on something that’s already obsolete. If you’re using VS2017, make sure you have the .NET Core cross-platform development workload installed.

Migrating the main library

We’re going to start a fresh new class library targeting .NET Core and move our code there. Actually, that statement is not entirely correct: if you open Visual Studio 2017, you’ll see that there are at least 3 different kinds of class library you can create (or more depending on additional tooling you may have installed):

  • Class Library (.NET Framework)
  • Class Library (.NET Core)
  • Class Library (.NET Standard)

This is very confusing and I’ve asked a question about this on Stack Overflow yesterday that attracted some pretty detailed answers. In short, if you want your class libraries to be as portable as possible, you need to target .NET Standard. .NET Standard is a specification detailing APIs that need to be available in compatible frameworks. .NET Core, and certain versions of the full .NET Framework, implement .NET Standard. However, they each also incorporate a lot of other runtime-related stuff, so targeting .NET Core specifically means you can’t use your code under the full .NET Framework.

So let’s create a project of type Class Library (.NET Standard). As always, this will create a solution with the same name as the project.

Next, we’ll delete the automatically created Class1 class, and copy the class files from the old Dandago.Finance library to the new project folder. You’ll notice that Visual Studio automatically notices the new files and includes them in the project, without you needing to explicitly add them:

 

Migrating the test project

Let’s add a new class library for the unit tests, but this time it needs to be a Class Library (.NET Core). If you get this wrong and choose Class Library (.NET Standard) instead, Visual Studio won’t find your tests and the dotnet test command will refuse to run it (as per this Stack Overflow question). The reason why .NET Standard won’t work for unit tests is detailed in the corresponding answer: in short, we need to specify a target framework that will be responsible for running the tests; .NET Standard on its own is not enough.

Next, we need to add a reference to the Dandago.Finance project.

Now, we can repeat the procedure we did for the main library, and delete Class1.cs and copy over the test classes.

However, this isn’t going to be as smooth as with the main library. The original test project uses NUnit, and at the time of writing, that isn’t fully supported by .NET Core. Fortunately, however, it’s easy to change to xUnit, which does already boast .NET Core support.

First, we need to install the following packages:

Install-Package Microsoft.NET.Test.Sdk
Install-Package xunit
Install-Package xunit.runner.visualstudio

Then, we need to make the following substitutions:

  1. using NUnit.Framework; becomes using Xunit;
  2. [TestFixture] goes away
  3. [Test] becomes [Fact]
  4. Assert.IsTrue(...) becomes Assert.True(...)
  5. Assert.IsFalse(...) becomes Assert.False(...)

The solution should now build, and the unit tests should run successfully:

Summary

Migrating Dandago.Finance to .NET Core has taught us a few things:

  1. Visual Studio can automatically detect new files for .NET Core / .NET Standard projects.
  2. Portable class libraries should target .NET Standard.
  3. Unit test projects should target .NET Core.
  4. Use xUnit for .NET Core unit tests.