John Mazouri

03/16/2017 (updated 10/15/2018)

.NET Core & the Linux Terminal

I’m a huge fan of .NET Core. However, being primarily a Windows user, I don’t have a ton of exposure to the development experience on Linux platforms. Most of my projects are developed in Visual Studio, deployed to a Linux server somewhere, and dotnet run from there.

So, when someone in a C# Discord server I frequent expressed some displeasure in the IDE-less development experience with .NET Core, I decided to look into it myself and write up this post for future developers. Let’s dive right in!

For this tutorial, I’ll be using Ubuntu 16.04 (though it shouldn’t matter past the installation step, assuming you’re using a distro that has .NET Core packages).

Installation

First, we need to install the .NET Core 2.1.0 SDK - follow the steps at this link for your distro. For Ubuntu 16.04, the steps are as follows:

wget -q https://packages.microsoft.com/config/ubuntu/16.04/packages-microsoft-prod.deb
sudo dpkg -i packages-microsoft-prod.deb

sudo apt-get install apt-transport-https
sudo apt-get update
sudo apt-get install dotnet-sdk-2.1

Creating a project template

.NET Core comes with a number of templates you can base your project on. To list these templates, run dotnet new.

Note: the first time you run a dotnet command, it’ll undergo an initial setup process. Just give it a moment. You can also opt to disable telemetry now, as described in the text that appears during this initial setup.

Once you’ve done that, you’ll see a list of various project types. For this tutorial, we’ll be working with a simple Console application, so run the following command in the directory of your choice (note the “Short Name” column in the list):

dotnet new console

This will create two files in the current directory: Program.cs and [foldername].csproj.

Staring at code

Those familiar with C# will recognize the files as a class file and project file, respectively. However, these aren’t your grandpa’s csproj files: it’s a new, much lighter form of them. Let’s open it up now, with the editor of your choice.

If you’re using a desktop interface, I highly recommend installing Visual Studio Code. It’s a free, open source editor with C# syntax highlighting & intellisense support, as well as extreme extensibility. If you prefer another editor, such as Vim, I suggest looking into Omnisharp, which provides similar features. Or, just wing it! That’s what compiler errors are for.

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

    <PropertyGroup>
        <OutputType>Exe</OutputType>
        <TargetFramework>netcoreapp2.1</TargetFramework>
    </PropertyGroup>

</Project>

A csproj file is simply an XML file detailing your project, how it should be compiled, what packages to include, etc. Let’s go over the file, line by line.

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

This is the root node of the csproj. The Sdk attribute designates what MSBuild targets your project will use - you typically don’t need to worry about this.

<OutputType>Exe</OutputType>

This tells the compiler to to output your project as an executable. The other common option is Library, which is also the default - this compiles your project as a managed DLL file, which, despite the name, is cross-platform. When using the Class Library template, this line is omitted.

<TargetFramework>netcoreapp2.1</TargetFramework>

This node designates what version of the runtime you’re targeting. As of writing. netcoreapp2.1 is the newest, though this will change as new versions are released. It’s usually good to leave it as-is.

There’s a number of options and other bits you can add to the csproj - some we’ll look into later. For more details, check out this article.

Writing code

Finally, the fun part! Open up the other file the template included, Program.cs. This contains your program’s entry point. “But wait!”, those familiar with the csproj format may be thinking, “how does the compiler know what files to include?”

Well, that’s a convenient change from before - by default, your project will include all the cs files within the project directory and subdirectories. This means you don’t have to specify each individual file to include anymore! (Fun note: MSBuild has had support for this forever, but they never added it to Visual Studio.)

For C# veterans: .NET Core doesn’t include all the libraries that you may be familiar with. Additionally, some of your favorite NuGet packages may not work, unless they’ve been updated with .NET Standard support - a topic that deserves its own post.

The default code will simply write the words “Hello World” to the console. Let’s try it out!

Running your code

To run your application, first we must install the packages it uses. Don’t worry - most of the BCL - “base class library” - comes with the tooling, so it will pull packages from the local cache instead of redownloading things. Run dotnet restore to install these packages, and dotnet run to build & run your program! Once you’re satisfied with the code (which can take anywhere from 5 minutes to 5 years), you can then package up your program for distribution!

If you just want to run your code, and don’t need to rebuild it, you can go into the ./bin/Debug/netcoreapp2.1 folder, and run dotnet [project].dll.

Publishing your project

One of the nice features provided by .NET Core is “Self-contained deployment”. Essentially, you can package your program, all the libraries it requires, and the runtime together, then deploy it to a server or distribute it to your users - with no further dependencies. To do so, we’ll need to start by editing our csproj by adding the following line within our PropertyGroup:

<RuntimeIdentifiers>ubuntu.16.04-x64;win10-x64</RuntimeIdentifiers>

This tells the build system that we intend to publish our project explicitly targeting Ubuntu 16.04 and Windows 10 (both 64-bit), by using those platforms’ “Runtime Identifiers”, or RIDs, separated by semicolons.

For a full list of potential targets, check out this list of Runtime Identifiers (RIDs).

Once you’ve added your target platforms, run dotnet restore. This will pull down all the platform-specific implementations of the runtime libraries our project uses. Finally, for each platform/RID you chose above, run the following command, which will build & publish your app in Release mode (including compiler optimizations):

dotnet publish -c Release -r [rid]

Finally, within your project folder, navigate to ./bin/Release/netcoreapp1.1/[rid]/publish. This folder will contain a bunch of dll files, a few json files, and your shiny new program executable, which will be named the same as your project folder. Note that your program code is actually contained within the [project].dll file within the folder - the executable is just a modified copy of the native dotnet CLI tool for your target platform, modified to automatically load the dll.

When publishing for Windows, your program executable will end in .exe - for Linux, it won’t have an extension. Regardless of your target platform, the [project.dll] file will run on all platforms when you execute it manually with the dotnet command, as described above.

This should give you a decent basis for starting out .NET Core development on Linux. Good luck, and have fun!