Creating a single file application in .NET Core
Tags: dotnet, netcore, productivity
Published: 2020-06-22 09:46:22

---
title: Creating a single file application in .NET Core
published: true
date: 2019-07-18 20:37:09 UTC
tags: dotnet,NETCore,Productivity
canonical_url: https://SimonReynolds.ie/creating-a-single-file-application-in-net-core/
---

 ![Creating a single file application in .NET Core](https://images.unsplash.com/photo-1465920431246-94bf7d9d4269?ixlib=rb-1.2.1&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1080&fit=max&ixid=eyJhcHBfaWQiOjExNzczfQ)

.NET Core has long supported the idea of a self-contained application, where the build process results in a single executable application. Until the release of .NET Core 3 preview 6 though, the resulting output would be a single executable and every supporting library or dll that it needed.

Let's examine the changes using a simple Hello World application...

<!--kg-card-begin: markdown-->
```bash
mkdir SingleExe
dotnet new console
```
<!--kg-card-end: markdown-->

This will create a simple console application with a `csproj` like below

<!--kg-card-begin: markdown-->
```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
  </PropertyGroup>

</Project>
```
<!--kg-card-end: markdown-->

If we add a `RuntimeIdentifier` for our environment we can then publish an application built for that environment that can be ran without requiring the .NET Core runtime to be installed. A breakdown of Runtime Identifiers can be found [here](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog).

Once we add the appropriate identifier, e.g. `<RuntimeIdentifier>osx-x64</RuntimeIdentifier>` we can then publish the application using

`dotnet publish -c Release`

This will result in a lot of output!

![Creating a single file application in .NET Core](https://simonreynolds.ie/content/images/2019/07/Screen-Shot-2019-07-18-at-21.25.18.png)<figcaption>72MB across 191 items is not a small self-contained application!</figcaption>

.NET Core 3 introduces a number of new options for consolidating all this output.

If we update our `csproj` to the following, then our output will be merged into a single executable and uncalled code will be trimmed from it

<!--kg-card-begin: markdown-->
```xml
<PublishTrimmed>true</PublishTrimmed>
<PublishSingleFile>true</PublishSingleFile>
```
<!--kg-card-end: markdown-->

![Creating a single file application in .NET Core](https://simonreynolds.ie/content/images/2019/07/Screen-Shot-2019-07-18-at-21.02.36.png)<figcaption>A single 30MB executable is a lot more manageable</figcaption>

Now, 30MB for an application that in this case simply writes "Hello World" to a console screen may seem excessive but remember that this is including every part of .NET Core that the application needs to run, it has absolutely no external dependencies!

> Be careful here though, anything that uses reflection may be incorrectly trimmed.

Test your applications after trimming them! You can tell the trimmer to include specific namespaces, classes or methods using the `TrimmerRootAssembly` element.

<!--kg-card-begin: markdown-->
```xml
<ItemGroup>
  <TrimmerRootAssembly Include="Namespace.To.Keep" />
</ItemGroup>
```
<!--kg-card-end: markdown-->

This is all still just using IL (Intermediate Language) though, the bytecode that .NET code compiles to and is JIT (Just In Time) compiled to the target machine's native code every time the application is ran.

If we're going to build for a specific platform, or offer builds for multiple platforms then it would make sense to pre-compile as much of the code to native machine code as we can.

Enter the `PublishReadyToRun` option. We include it in our `csproj` file so that the whole file now looks like this...

<!--kg-card-begin: markdown-->
```xml
<Project Sdk="Microsoft.NET.Sdk">

  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp3.0</TargetFramework>
    <PublishTrimmed>true</PublishTrimmed>
    <PublishSingleFile>true</PublishSingleFile>
    <PublishReadyToRun>true</PublishReadyToRun>
    <!-- Set the RuntimeIdentifier below to match your own system -->
    <RuntimeIdentifier>osx-x64</RuntimeIdentifier>
  </PropertyGroup>

</Project>
```
<!--kg-card-end: markdown-->

Now the compiler will pre-compile as much code as it can to (in this case) Mac specific machine code. This will not have to be JITted each time the application runs, leading to a noticeably faster application start.

The tradeoff here is that the resulting executables are now platform specific but if you can provide a pre-built executable for each of the major platforms then it could be well worth considering.