.NET Core introduced one very useful deployment option: self-contained application deployment. This means that deployment package contains all the required artifacts to run the application. I can simply copy deploy output to target machine and run it. No runtime prerequisites needed as it is the case with .NET Framework.
Furthermore, .NET Core 3.0 introduced self-contained executables. This means that I can can publish application as a executable and run it without dotnet tool. Before you need to use dotnet run command to start an application. .Net Core 3.0+ also enabled some additional features to reduced size of deployment, reduce startup times and enhance runtime performances.
Enough said, let’s dig in and investigate some deployment options in .NET Core 3.1.
For this experiment, I used simple, empty Windows Presentation Foundation (WPF) .NET Core 3.1 application. Any other type of application can be used also: console, web, other desktop stack (e.g. Windows.Forms). The only difference will be the output: different application types require different dependencies, thus different output.
I want to investigate different deployment options of the .NET Core application, like build times, output sizes, number of files in deploy output, dependencies, etc…
MSBuild command to deploy .NET Core application
I almost always use MSBuild build engine for building .NET application. My experiment in this blog is no exception. I used following command with several different deployment parameters:
1 2 3 4 5 6 7 8 9 10 11 |
msbuild Jenx.NetCore3.PublishStrategies.sln /t:"Restore,Build,Publish" /p:Configuration=Release /p:Platform="Any Cpu" /p:PublishProfile="<profile>" /p:SelfContained=true | false /p:RuntimeIdentifier=win-x64 /p:PublishSingleFile=true | false /p:PublishReadyToRun=true | false /p:PublishTrimmed=true | false /p:OutputPath="<output folder>" |
Testing script
I created PowerShell script which cleans temporary build items, executes msbuild build target, prepares deploy outputs and performs timing of the deployments. Script output also enables more friendly comparison of the results.
Here is my script. It’s put together on ad-hoc, not perfect, but it fits the purpose.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 |
$PSScriptRoot = Split-Path -Parent -Path $MyInvocation.MyCommand.Definition function Get-FolderSize($Path) { $FolderSizeInMb = ((Get-ChildItem $Path -Recurse | Measure-Object -Property Length -Sum -ErrorAction Stop).Sum / 1MB) $FolderSizeInMbFormatted = [math]::Round($FolderSizeInMb, 2) "{0} MB" -f $FolderSizeInMbFormatted } function Get-FilesCount($Path) { ( Get-ChildItem $Path -Recurse -File | Measure-Object ).Count; } function Remove-BinariesDirs { if (Test-Path "$PSScriptRoot\src\bin") { Write-Host "Deleting bin folder" Remove-Item -Path "$PSScriptRoot\src\bin" -Recurse -Force } if (Test-Path "$PSScriptRoot\src\obj") { Write-Host "Deleting obj folder" Remove-Item -Path "$PSScriptRoot\src\obj" -Recurse -Force } } function Remove-PublishDir { if (Test-Path "$PSScriptRoot\src\Publish") { Write-Host "Deleting Publish folder" Remove-Item -Path "$PSScriptRoot\src\Publish" -Recurse -Force } } function Start-BuildWithStatistics($BuildDescription, $Command, $OutputPath) { Write-Host "Starting build: " $BuildDescription Write-Host $Command $DeployOutputSize = Get-FolderSize $PSScriptRoot"\src\"$OutputPath"publish" $DeployOutputFilesNo = Get-FilesCount $PSScriptRoot"\src\"$OutputPath"publish" $BuildStatistics = New-Object System.Object $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Build Description" -Value $BuildDescription $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Output size" -Value $DeployOutputSize $BuildStatistics | Add-Member -MemberType NoteProperty -Name "No of files in output" -Value $DeployOutputFilesNo return $BuildStatistics } function Run-DeployAnalysis { Remove-BinariesDirs Remove-PublishDir $BuildStatisticList = New-Object System.Collections.ArrayList $Stopwatch = [system.diagnostics.stopwatch]::StartNew() $OutputPath = "Publish/AnyCpu" $Command = msbuild Jenx.NetCore3.PublishStrategies.sln /t:'Restore,Build,Publish' /p:Configuration=Release /p:Platform='Any Cpu' /p:PublishProfile=AnyCpu-Profile /p:OutputPath=$OutputPath $BuildStatistics = Start-BuildWithStatistics "Runtime dependent build- default" $Command $OutputPath $Stopwatch.Stop() $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Execution time" -Value $Stopwatch.Elapsed $BuildStatisticList.Add($BuildStatistics) | Out-Null Remove-BinariesDirs $Stopwatch.Restart(); $OutputPath = "Publish/AnyCpu-SelfContained" $Command = msbuild Jenx.NetCore3.PublishStrategies.sln /t:'Restore,Build,Publish' /p:Configuration=Release /p:Platform='Any Cpu' /p:PublishProfile=AnyCpu-SelfContained-Profile /p:SelfContained=true /p:RuntimeIdentifier=win-x64 /p:OutputPath=$OutputPath $BuildStatistics = Start-BuildWithStatistics "Self-contained build" $Command $OutputPath $Stopwatch.Stop() $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Execution time" -Value $Stopwatch.Elapsed $BuildStatisticList.Add($BuildStatistics) | Out-Null Remove-BinariesDirs $Stopwatch.Restart(); $OutputPath = "Publish/AnyCpu-SelfContained-SingleFile" $Command = msbuild Jenx.NetCore3.PublishStrategies.sln /t:"Restore,Build,Publish" /p:Configuration=Release /p:Platform="Any Cpu" /p:PublishProfile=AnyCpu-SelfContained-SingleFile-Profile /p:SelfContained=true /p:RuntimeIdentifier=win-x64 /p:PublishSingleFile=true /p:OutputPath=$OutputPath $BuildStatistics = Start-BuildWithStatistics "Self-contained, single file build" $Command $OutputPath $Stopwatch.Stop() $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Execution time" -Value $Stopwatch.Elapsed $BuildStatisticList.Add($BuildStatistics) | Out-Null Remove-BinariesDirs $Stopwatch.Restart(); $OutputPath = "Publish/AnyCpu-SelfContained-SingleFile-ReadyToRun" $Command = msbuild Jenx.NetCore3.PublishStrategies.sln /t:"Restore,Build,Publish" /p:Configuration=Release /p:Platform="Any Cpu" /p:PublishProfile=AnyCpu-SelfContained-SingleFile-ReadyToRun-Profile /p:SelfContained=true /p:RuntimeIdentifier=win-x64 /p:PublishSingleFile=true /p:PublishReadyToRun=true /p:OutputPath=$OutputPath $BuildStatistics = Start-BuildWithStatistics "Self-contained, single file, ready-to-run build" $Command $OutputPath $Stopwatch.Stop() $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Execution time" -Value $Stopwatch.Elapsed $BuildStatisticList.Add($BuildStatistics) | Out-Null Remove-BinariesDirs $Stopwatch.Restart(); $OutputPath = "Publish/AnyCpu-SelfContained-SingleFile-Trimmed" $Command = msbuild Jenx.NetCore3.PublishStrategies.sln /t:"Restore,Build,Publish" /p:Configuration=Release /p:Platform="Any Cpu" /p:PublishProfile=AnyCpu-SelfContained-SingleFile-Trimmed-Profile /p:SelfContained=true /p:RuntimeIdentifier=win-x64 /p:PublishSingleFile=true /p:PublishReadyToRun=false /p:PublishTrimmed=true /p:OutputPath=$OutputPath $BuildStatistics = Start-BuildWithStatistics "Self-contained, single file, trimmed build" $Command $OutputPath $Stopwatch.Stop() $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Execution time" -Value $Stopwatch.Elapsed $BuildStatisticList.Add($BuildStatistics) | Out-Null Remove-BinariesDirs $Stopwatch.Restart(); $OutputPath = "Publish/AnyCpu-SelfContained-SingleFile-ReadyToRun-Trimmed" $Command = msbuild Jenx.NetCore3.PublishStrategies.sln /t:"Restore,Build,Publish" /p:Configuration=Release /p:Platform="Any Cpu" /p:PublishProfile=AnyCpu-SelfContained-SingleFile-ReadyToRun-Trimmed-Profile /p:SelfContained=true /p:RuntimeIdentifier=win-x64 /p:PublishSingleFile=true /p:PublishReadyToRun=true /p:PublishTrimmed=true /p:OutputPath=$OutputPath $BuildStatistics = Start-BuildWithStatistics "Self-contained, single file, ready-to-run, trimmed build" $Command $OutputPath $Stopwatch.Stop() $BuildStatistics | Add-Member -MemberType NoteProperty -Name "Execution time" -Value $Stopwatch.Elapsed $BuildStatisticList.Add($BuildStatistics) | Out-Null Remove-BinariesDirs $BuildStatisticList | Format-Table -AutoSize } Run-DeployAnalysis |
Output
Let’s start from the end, and first look at my deploy statistic. Later on I will comment all these output results.
1 2 3 4 5 6 7 8 |
Build Description Output size No of files in output Execution time ----------------- ----------- --------------------- -------------- Runtime dependent build- default 0,17 MB 5 00:00:05.3388528 Self-contained build 149,21 MB 499 00:00:11.4329536 Self-contained, single file build 149,24 MB 2 00:00:10.7067793 Self-contained, single file, ready-to-run build 149,24 MB 2 00:00:10.9578949 Self-contained, single file, trimmed build 86,69 MB 2 00:01:24.2830539 Self-contained, single file, ready-to-run, trimmed build 136,44 MB 2 00:03:14.0493842 |
Output of the runtime dependent build is the smallest. It’s only 0,17MB in size. And this build & deploy took only 5 seconds. This makes sense: compiler just compiles source code, includes any external dependencies (in my case none) and expects you to have target .NET Core runtime installed on the end-user machine.
All self-contained builds run longer, from 10 sec to 3 minutes, depending on deploy settings. Trimmed & ready-to-run deployment version took even more then 2 minutes to build – and that’s only for a simple WPF .NET Core application. But again, self-contained (platform specific compile & putting all dependencies into deploy), trimming (removing unused code from the app) and ready-to-run option (adding performant native code into output) can be tricky and time consuming. Thus, it makes sense that linking together all required artifacts takes a bit longer compared to simple compile.
Let’s take a look of deploy outputs created by my PowerShell script in more details.
Runtime or framework dependent deployment
This is default option. The output is minimalistic and dependent on .NET Core runtime. If .NET Core is not installed on the target machine, the application will not work.
The output of my simple empty WPF application:
1 2 3 4 5 6 7 |
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7. 02. 2020 11:29 485 Jenx.NetCore3.PublishStrategies.deps.json -a---- 7. 02. 2020 11:29 7680 Jenx.NetCore3.PublishStrategies.dll -a---- 7. 02. 2020 11:29 170496 Jenx.NetCore3.PublishStrategies.exe -a---- 7. 02. 2020 11:29 1844 Jenx.NetCore3.PublishStrategies.pdb -a---- 7. 02. 2020 11:29 161 Jenx.NetCore3.PublishStrategies.runtimeconfig.json |
In my experiment, the size of simple WPF app is only about 170kB!!!! Also, application compiles very quickly, because compiler does not need to deal with deployment or linking of any assemblies from runtime. It only compiles source code and produce binaries.
This options is very handy in enterprise or cloud (Azure) environments when I know that runtime is already present.
Benefits of this type of deployment:
- Deployed applications are small.
- Applications are sharing runtime, thus memory and disk footprint is small when deploying several applications.
- Only dependencies not included in runtime needs to be deployed (e.g. 3rd party assemblies, normally included with NuGet packing tool).
- Deployed applications use .NET assembly file format which is CPU and OS agnostic (https://docs.microsoft.com/en-us/dotnet/standard/assembly/file-format). I only need to target correct .NET Core runtime.
- It uses latest patched versions of runtime, with all the latest benefits (this could be also dangerous!).
Some drawbacks:
- If .NET Core runtime is not installed on target environment, then deployed application will not work.
- It can work on newer runtimes (all patches) as it was compiled against (this is generally a good thing), but application behave strange if target runtime version is different then compile runtime version.
- If runtime is not present on the end user machine, deployment can be tricky task. By using Installers which checks and deploy all prerequisites and dependencies this can be more professional and more elegant.
In general, this deployment is more or less the same as it is deployment of .NET Framework applications. So, If you do .NET Framework application deployment, this scenario should be familiar to you.
Self-contained executable
When you execute a self-contained deployment, the .NET Core SDK creates a platform-specific binaries. You are not just targeting only .NET Core version (or .NET Standard) but you create platform specific binaries. On short: If I create self-contained console application targeting linux-x64
environment, it will no run on my win10-x64
machine. Even though, let’s say, I target .NET Core 3.0.
Thus, when creating self-contained applications/executables runtime identifier must be set (check RIDs here: https://docs.microsoft.com/en-us/dotnet/core/rid-catalog) .
This deploy output can be simply copied to target device and started. Even more, two or more application can run side by side without worrying about runtimes installed on target machine. This deploy does not need any dependency on preinstall .NET Core runtimes. The bad thing (from packet size perspective) is that all dependencies are included in the deploy. Even more, if I ship next version of my app, it will again contain all the required assemblies.
In my experiment, my demo WPF application is now approx 150MB in size!!!!! The difference between this version and platform dependent deploy is huge. Next listing are files included in self-contained app – just to get a feeling :). Basically whole .NET Core runtime, some target native assemblies and all WPF dependencies are included in deploy.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 |
Mode LastWriteTime Length Name ---- ------------- ------ ---- d----- 7. 02. 2020 11:29 cs d----- 7. 02. 2020 11:29 de d----- 7. 02. 2020 11:29 es d----- 7. 02. 2020 11:29 fr d----- 7. 02. 2020 11:29 it d----- 7. 02. 2020 11:29 ja d----- 7. 02. 2020 11:29 ko d----- 7. 02. 2020 11:29 pl d----- 7. 02. 2020 11:29 pt-BR d----- 7. 02. 2020 11:29 ru d----- 7. 02. 2020 11:29 tr d----- 7. 02. 2020 11:29 zh-Hans d----- 7. 02. 2020 11:29 zh-Hant -a---- 15. 11. 2019 15:50 19832 Accessibility.dll -a---- 20. 04. 2018 07:28 19208 api-ms-win-core-console-l1-1-0.dll -a---- 20. 04. 2018 07:28 18696 api-ms-win-core-datetime-l1-1-0.dll -a---- 20. 04. 2018 07:28 18696 api-ms-win-core-debug-l1-1-0.dll -a---- 20. 04. 2018 07:28 18696 api-ms-win-core-errorhandling-l1-1-0.dll -a---- 20. 04. 2018 07:29 22280 api-ms-win-core-file-l1-1-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-file-l1-2-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-file-l2-1-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-handle-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-core-heap-l1-1-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-interlocked-l1-1-0.dll -a---- 20. 04. 2018 07:37 19720 api-ms-win-core-libraryloader-l1-1-0.dll -a---- 20. 04. 2018 07:37 21256 api-ms-win-core-localization-l1-2-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-core-memory-l1-1-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-namedpipe-l1-1-0.dll -a---- 20. 04. 2018 07:37 19720 api-ms-win-core-processenvironment-l1-1-0.dll -a---- 20. 04. 2018 07:37 20744 api-ms-win-core-processthreads-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-core-processthreads-l1-1-1.dll -a---- 20. 04. 2018 07:37 18184 api-ms-win-core-profile-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-core-rtlsupport-l1-1-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-string-l1-1-0.dll -a---- 20. 04. 2018 07:37 20744 api-ms-win-core-synch-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-core-synch-l1-2-0.dll -a---- 20. 04. 2018 07:37 19720 api-ms-win-core-sysinfo-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-core-timezone-l1-1-0.dll -a---- 20. 04. 2018 07:37 18696 api-ms-win-core-util-l1-1-0.dll -a---- 20. 04. 2018 07:37 19720 api-ms-win-crt-conio-l1-1-0.dll -a---- 20. 04. 2018 07:37 22792 api-ms-win-crt-convert-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-crt-environment-l1-1-0.dll -a---- 20. 04. 2018 07:37 20744 api-ms-win-crt-filesystem-l1-1-0.dll -a---- 20. 04. 2018 07:37 19720 api-ms-win-crt-heap-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-crt-locale-l1-1-0.dll -a---- 20. 04. 2018 07:37 27912 api-ms-win-crt-math-l1-1-0.dll -a---- 20. 04. 2018 07:37 26888 api-ms-win-crt-multibyte-l1-1-0.dll -a---- 20. 04. 2018 07:37 71432 api-ms-win-crt-private-l1-1-0.dll -a---- 20. 04. 2018 07:37 19720 api-ms-win-crt-process-l1-1-0.dll -a---- 20. 04. 2018 07:37 23304 api-ms-win-crt-runtime-l1-1-0.dll -a---- 20. 04. 2018 07:37 24840 api-ms-win-crt-stdio-l1-1-0.dll -a---- 20. 04. 2018 07:37 24840 api-ms-win-crt-string-l1-1-0.dll -a---- 20. 04. 2018 07:37 21256 api-ms-win-crt-time-l1-1-0.dll -a---- 20. 04. 2018 07:37 19208 api-ms-win-crt-utility-l1-1-0.dll -a---- 15. 11. 2019 08:32 747592 clrcompression.dll -a---- 15. 11. 2019 01:06 243784 clretwrc.dll -a---- 15. 11. 2019 01:08 1304648 clrjit.dll -a---- 15. 11. 2019 01:06 5589576 coreclr.dll -a---- 1. 01. 1980 00:00 4481992 D3DCompiler_47_cor3.dll -a---- 15. 11. 2019 01:06 136264 dbgshim.dll -a---- 15. 11. 2019 15:50 493944 DirectWriteForwarder.dll -a---- 15. 11. 2019 15:51 599416 hostfxr.dll -a---- 15. 11. 2019 15:51 590712 hostpolicy.dll -a---- 7. 02. 2020 11:29 39143 Jenx.NetCore3.PublishStrategies.deps.json -a---- 7. 02. 2020 11:29 6656 Jenx.NetCore3.PublishStrategies.dll -a---- 7. 02. 2020 11:29 170496 Jenx.NetCore3.PublishStrategies.exe -a---- 7. 02. 2020 11:29 1860 Jenx.NetCore3.PublishStrategies.pdb -a---- 7. 02. 2020 11:29 281 Jenx.NetCore3.PublishStrategies.runtimeconfig.json -a---- 15. 11. 2019 15:51 797560 Microsoft.CSharp.dll -a---- 4. 12. 2017 17:36 1495800 Microsoft.DiaSymReader.Native.amd64.dll -a---- 15. 11. 2019 15:51 1209720 Microsoft.VisualBasic.Core.dll -a---- 15. 11. 2019 15:51 16248 Microsoft.VisualBasic.dll -a---- 15. 11. 2019 15:51 21368 Microsoft.Win32.Primitives.dll -a---- 15. 11. 2019 15:50 21368 Microsoft.Win32.Registry.AccessControl.dll -a---- 15. 11. 2019 15:51 82808 Microsoft.Win32.Registry.dll -a---- 15. 11. 2019 15:50 78200 Microsoft.Win32.SystemEvents.dll -a---- 15. 11. 2019 01:06 1263672 mscordaccore.dll -a---- 15. 11. 2019 01:06 1263672 mscordaccore_amd64_amd64_4.700.19.56402.dll -a---- 15. 11. 2019 01:08 1318472 mscordbi.dll -a---- 15. 11. 2019 08:41 57208 mscorlib.dll -a---- 15. 11. 2019 01:06 178760 mscorrc.debug.dll -a---- 15. 11. 2019 01:05 13384 mscorrc.dll -a---- 15. 11. 2019 15:51 114040 netstandard.dll -a---- 1. 01. 1980 00:00 159608 PenImc_cor3.dll -a---- 15. 11. 2019 15:51 8710008 PresentationCore.dll -a---- 15. 11. 2019 15:50 457592 PresentationFramework.Aero.dll -a---- 15. 11. 2019 15:50 463736 PresentationFramework.Aero2.dll -a---- 15. 11. 2019 15:50 228728 PresentationFramework.AeroLite.dll -a---- 15. 11. 2019 15:51 268152 PresentationFramework.Classic.dll -a---- 15. 11. 2019 15:51 15768440 PresentationFramework.dll -a---- 15. 11. 2019 15:51 676728 PresentationFramework.Luna.dll -a---- 15. 11. 2019 15:50 331640 PresentationFramework.Royale.dll -a---- 15. 11. 2019 15:50 25464 PresentationFramework-SystemCore.dll -a---- 15. 11. 2019 15:50 22904 PresentationFramework-SystemData.dll -a---- 15. 11. 2019 15:50 21880 PresentationFramework-SystemDrawing.dll -a---- 15. 11. 2019 15:50 22392 PresentationFramework-SystemXml.dll -a---- 15. 11. 2019 15:50 17784 PresentationFramework-SystemXmlLinq.dll -a---- 1. 01. 1980 00:00 1224056 PresentationNative_cor3.dll -a---- 15. 11. 2019 15:50 1303416 PresentationUI.dll -a---- 15. 11. 2019 15:51 1650552 ReachFramework.dll -a---- 15. 11. 2019 00:43 277 SOS_README.md -a---- 15. 11. 2019 15:51 13688 System.AppContext.dll -a---- 15. 11. 2019 15:51 14200 System.Buffers.dll -a---- 15. 11. 2019 15:51 481144 System.CodeDom.dll -a---- 15. 11. 2019 15:51 188792 System.Collections.Concurrent.dll -a---- 15. 11. 2019 15:51 331128 System.Collections.dll -a---- 15. 11. 2019 15:51 648056 System.Collections.Immutable.dll -a---- 15. 11. 2019 15:51 96632 System.Collections.NonGeneric.dll -a---- 15. 11. 2019 15:51 89976 System.Collections.Specialized.dll -a---- 15. 11. 2019 15:51 162168 System.ComponentModel.Annotations.dll -a---- 15. 11. 2019 15:51 15736 System.ComponentModel.DataAnnotations.dll -a---- 15. 11. 2019 15:51 15736 System.ComponentModel.dll -a---- 15. 11. 2019 15:51 34168 System.ComponentModel.EventBasedAsync.dll -a---- 15. 11. 2019 15:51 53112 System.ComponentModel.Primitives.dll -a---- 15. 11. 2019 15:51 706936 System.ComponentModel.TypeConverter.dll -a---- 15. 11. 2019 15:50 981880 System.Configuration.ConfigurationManager.dll -a---- 15. 11. 2019 15:51 18296 System.Configuration.dll -a---- 15. 11. 2019 15:51 152440 System.Console.dll -a---- 15. 11. 2019 15:51 23416 System.Core.dll -a---- 15. 11. 2019 15:51 2912632 System.Data.Common.dll -a---- 15. 11. 2019 15:51 14200 System.Data.DataSetExtensions.dll -a---- 15. 11. 2019 15:51 25464 System.Data.dll -a---- 15. 11. 2019 15:50 19832 System.Design.dll -a---- 15. 11. 2019 15:51 14712 System.Diagnostics.Contracts.dll -a---- 15. 11. 2019 15:51 14200 System.Diagnostics.Debug.dll -a---- 15. 11. 2019 15:51 99192 System.Diagnostics.DiagnosticSource.dll -a---- 15. 11. 2019 15:50 302968 System.Diagnostics.EventLog.dll -a---- 15. 11. 2019 15:51 29048 System.Diagnostics.FileVersionInfo.dll -a---- 15. 11. 2019 15:50 268664 System.Diagnostics.PerformanceCounter.dll -a---- 15. 11. 2019 15:51 257400 System.Diagnostics.Process.dll -a---- 15. 11. 2019 15:51 35192 System.Diagnostics.StackTrace.dll -a---- 15. 11. 2019 15:51 55672 System.Diagnostics.TextWriterTraceListener.dll -a---- 15. 11. 2019 15:51 16760 System.Diagnostics.Tools.dll -a---- 15. 11. 2019 15:51 125304 System.Diagnostics.TraceSource.dll -a---- 15. 11. 2019 15:51 15224 System.Diagnostics.Tracing.dll -a---- 15. 11. 2019 15:50 1036152 System.DirectoryServices.dll -a---- 15. 11. 2019 15:51 54136 System.dll -a---- 15. 11. 2019 15:50 965496 System.Drawing.Common.dll -a---- 15. 11. 2019 15:50 14200 System.Drawing.Design.dll -a---- 15. 11. 2019 15:50 20856 System.Drawing.dll -a---- 15. 11. 2019 15:51 123768 System.Drawing.Primitives.dll -a---- 15. 11. 2019 15:51 15224 System.Dynamic.Runtime.dll -a---- 15. 11. 2019 15:51 14712 System.Globalization.Calendars.dll -a---- 15. 11. 2019 15:51 14200 System.Globalization.dll -a---- 15. 11. 2019 15:51 14712 System.Globalization.Extensions.dll -a---- 15. 11. 2019 15:51 69496 System.IO.Compression.Brotli.dll -a---- 15. 11. 2019 15:51 249208 System.IO.Compression.dll -a---- 15. 11. 2019 15:51 14200 System.IO.Compression.FileSystem.dll -a---- 15. 11. 2019 15:51 34168 System.IO.Compression.ZipFile.dll -a---- 15. 11. 2019 15:51 14200 System.IO.dll -a---- 15. 11. 2019 15:51 86904 System.IO.FileSystem.AccessControl.dll -a---- 15. 11. 2019 15:51 217464 System.IO.FileSystem.dll -a---- 15. 11. 2019 15:51 37752 System.IO.FileSystem.DriveInfo.dll -a---- 15. 11. 2019 15:51 14200 System.IO.FileSystem.Primitives.dll -a---- 15. 11. 2019 15:51 68984 System.IO.FileSystem.Watcher.dll -a---- 15. 11. 2019 15:51 79224 System.IO.IsolatedStorage.dll -a---- 15. 11. 2019 15:51 63352 System.IO.MemoryMappedFiles.dll -a---- 15. 11. 2019 15:51 271736 System.IO.Packaging.dll -a---- 15. 11. 2019 15:51 14200 System.IO.Pipes.AccessControl.dll -a---- 15. 11. 2019 15:51 130424 System.IO.Pipes.dll -a---- 15. 11. 2019 15:51 14200 System.IO.UnmanagedMemoryStream.dll -a---- 15. 11. 2019 15:51 420728 System.Linq.dll -a---- 15. 11. 2019 15:51 5256568 System.Linq.Expressions.dll -a---- 15. 11. 2019 15:51 177528 System.Linq.Queryable.dll -a---- 15. 11. 2019 15:51 180088 System.Memory.dll -a---- 15. 11. 2019 15:51 16248 System.Net.dll -a---- 15. 11. 2019 15:51 1475448 System.Net.Http.dll -a---- 15. 11. 2019 15:51 653688 System.Net.HttpListener.dll -a---- 15. 11. 2019 15:51 545144 System.Net.Mail.dll -a---- 15. 11. 2019 15:51 86392 System.Net.NameResolution.dll -a---- 15. 11. 2019 15:51 174968 System.Net.NetworkInformation.dll -a---- 15. 11. 2019 15:51 97656 System.Net.Ping.dll -a---- 15. 11. 2019 15:50 212856 System.Net.Primitives.dll -a---- 15. 11. 2019 15:50 346488 System.Net.Requests.dll -a---- 15. 11. 2019 15:51 572792 System.Net.Security.dll -a---- 15. 11. 2019 15:50 33144 System.Net.ServicePoint.dll -a---- 15. 11. 2019 15:51 539512 System.Net.Sockets.dll -a---- 15. 11. 2019 15:50 158584 System.Net.WebClient.dll -a---- 15. 11. 2019 15:51 66424 System.Net.WebHeaderCollection.dll -a---- 15. 11. 2019 15:51 25976 System.Net.WebProxy.dll -a---- 15. 11. 2019 15:51 71032 System.Net.WebSockets.Client.dll -a---- 15. 11. 2019 15:51 140664 System.Net.WebSockets.dll -a---- 15. 11. 2019 15:50 14200 System.Numerics.dll -a---- 15. 11. 2019 15:50 149368 System.Numerics.Vectors.dll -a---- 15. 11. 2019 15:50 87416 System.ObjectModel.dll -a---- 15. 11. 2019 15:51 972152 System.Printing.dll -a---- 15. 11. 2019 01:06 9556040 System.Private.CoreLib.dll -a---- 15. 11. 2019 15:51 2060152 System.Private.DataContractSerialization.dll -a---- 15. 11. 2019 15:51 241528 System.Private.Uri.dll -a---- 15. 11. 2019 15:51 8423800 System.Private.Xml.dll -a---- 15. 11. 2019 15:50 370552 System.Private.Xml.Linq.dll -a---- 15. 11. 2019 15:50 68984 System.Reflection.DispatchProxy.dll -a---- 15. 11. 2019 15:50 15224 System.Reflection.dll -a---- 15. 11. 2019 15:51 14200 System.Reflection.Emit.dll -a---- 15. 11. 2019 15:51 14200 System.Reflection.Emit.ILGeneration.dll -a---- 15. 11. 2019 15:51 14200 System.Reflection.Emit.Lightweight.dll -a---- 15. 11. 2019 15:51 14200 System.Reflection.Extensions.dll -a---- 15. 11. 2019 15:51 1070968 System.Reflection.Metadata.dll -a---- 15. 11. 2019 15:51 14712 System.Reflection.Primitives.dll -a---- 15. 11. 2019 15:51 28536 System.Reflection.TypeExtensions.dll -a---- 15. 11. 2019 15:51 117624 System.Resources.Extensions.dll -a---- 15. 11. 2019 15:51 14200 System.Resources.Reader.dll -a---- 15. 11. 2019 15:51 14712 System.Resources.ResourceManager.dll -a---- 15. 11. 2019 15:50 41848 System.Resources.Writer.dll -a---- 15. 11. 2019 15:50 18808 System.Runtime.CompilerServices.Unsafe.dll -a---- 15. 11. 2019 15:50 17272 System.Runtime.CompilerServices.VisualC.dll -a---- 15. 11. 2019 15:50 53112 System.Runtime.dll -a---- 15. 11. 2019 15:51 206200 System.Runtime.Extensions.dll -a---- 15. 11. 2019 15:51 14200 System.Runtime.Handles.dll -a---- 15. 11. 2019 15:51 53112 System.Runtime.InteropServices.dll -a---- 15. 11. 2019 15:51 27000 System.Runtime.InteropServices.RuntimeInformation.dll -a---- 15. 11. 2019 15:51 15224 System.Runtime.InteropServices.WindowsRuntime.dll -a---- 15. 11. 2019 15:51 14712 System.Runtime.Intrinsics.dll -a---- 15. 11. 2019 15:51 14200 System.Runtime.Loader.dll -a---- 15. 11. 2019 15:51 196472 System.Runtime.Numerics.dll -a---- 15. 11. 2019 15:51 15736 System.Runtime.Serialization.dll -a---- 15. 11. 2019 15:51 308600 System.Runtime.Serialization.Formatters.dll -a---- 15. 11. 2019 15:51 14712 System.Runtime.Serialization.Json.dll -a---- 15. 11. 2019 15:51 25464 System.Runtime.Serialization.Primitives.dll -a---- 15. 11. 2019 15:51 15224 System.Runtime.Serialization.Xml.dll -a---- 15. 11. 2019 15:51 355192 System.Runtime.WindowsRuntime.dll -a---- 15. 11. 2019 15:51 75128 System.Runtime.WindowsRuntime.UI.Xaml.dll -a---- 15. 11. 2019 15:51 211320 System.Security.AccessControl.dll -a---- 15. 11. 2019 15:51 88952 System.Security.Claims.dll -a---- 15. 11. 2019 15:51 691064 System.Security.Cryptography.Algorithms.dll -a---- 15. 11. 2019 15:51 431992 System.Security.Cryptography.Cng.dll -a---- 15. 11. 2019 15:51 177528 System.Security.Cryptography.Csp.dll -a---- 15. 11. 2019 15:51 70520 System.Security.Cryptography.Encoding.dll -a---- 15. 11. 2019 15:51 32632 System.Security.Cryptography.OpenSsl.dll -a---- 15. 11. 2019 15:51 839032 System.Security.Cryptography.Pkcs.dll -a---- 15. 11. 2019 15:50 101752 System.Security.Cryptography.Primitives.dll -a---- 15. 11. 2019 15:51 33656 System.Security.Cryptography.ProtectedData.dll -a---- 15. 11. 2019 15:51 464248 System.Security.Cryptography.X509Certificates.dll -a---- 15. 11. 2019 15:51 424312 System.Security.Cryptography.Xml.dll -a---- 15. 11. 2019 15:51 17272 System.Security.dll -a---- 15. 11. 2019 15:51 159608 System.Security.Permissions.dll -a---- 15. 11. 2019 15:51 14200 System.Security.Principal.dll -a---- 15. 11. 2019 15:51 144760 System.Security.Principal.Windows.dll -a---- 15. 11. 2019 15:51 14200 System.Security.SecureString.dll -a---- 15. 11. 2019 15:51 15736 System.ServiceModel.Web.dll -a---- 15. 11. 2019 15:51 14712 System.ServiceProcess.dll -a---- 15. 11. 2019 15:51 858488 System.Text.Encoding.CodePages.dll -a---- 15. 11. 2019 15:51 14712 System.Text.Encoding.dll -a---- 15. 11. 2019 15:51 14200 System.Text.Encoding.Extensions.dll -a---- 15. 11. 2019 15:51 99192 System.Text.Encodings.Web.dll -a---- 15. 11. 2019 15:51 823672 System.Text.Json.dll -a---- 15. 11. 2019 15:51 393080 System.Text.RegularExpressions.dll -a---- 15. 11. 2019 15:50 40312 System.Threading.AccessControl.dll -a---- 15. 11. 2019 15:51 111480 System.Threading.Channels.dll -a---- 15. 11. 2019 15:51 76664 System.Threading.dll -a---- 15. 11. 2019 15:51 15224 System.Threading.Overlapped.dll -a---- 15. 11. 2019 15:51 478584 System.Threading.Tasks.Dataflow.dll -a---- 15. 11. 2019 15:51 15736 System.Threading.Tasks.dll -a---- 15. 11. 2019 15:51 14712 System.Threading.Tasks.Extensions.dll -a---- 15. 11. 2019 15:51 107896 System.Threading.Tasks.Parallel.dll -a---- 15. 11. 2019 15:50 16760 System.Threading.Thread.dll -a---- 15. 11. 2019 15:50 14200 System.Threading.ThreadPool.dll -a---- 15. 11. 2019 15:50 14200 System.Threading.Timer.dll -a---- 15. 11. 2019 15:50 15224 System.Transactions.dll -a---- 15. 11. 2019 15:50 348024 System.Transactions.Local.dll -a---- 15. 11. 2019 15:50 14200 System.ValueTuple.dll -a---- 15. 11. 2019 15:50 13688 System.Web.dll -a---- 15. 11. 2019 15:50 44920 System.Web.HttpUtility.dll -a---- 15. 11. 2019 15:51 1470840 System.Windows.Controls.Ribbon.dll -a---- 15. 11. 2019 15:50 14200 System.Windows.dll -a---- 15. 11. 2019 15:51 118136 System.Windows.Extensions.dll -a---- 15. 11. 2019 15:51 4873080 System.Windows.Forms.Design.dll -a---- 15. 11. 2019 15:51 543096 System.Windows.Forms.Design.Editors.dll -a---- 15. 11. 2019 15:51 13419896 System.Windows.Forms.dll -a---- 15. 11. 2019 15:51 127352 System.Windows.Input.Manipulations.dll -a---- 15. 11. 2019 15:51 18296 System.Windows.Presentation.dll -a---- 15. 11. 2019 15:51 1430904 System.Xaml.dll -a---- 15. 11. 2019 15:50 23928 System.Xml.dll -a---- 15. 11. 2019 15:50 14712 System.Xml.Linq.dll -a---- 15. 11. 2019 15:50 21368 System.Xml.ReaderWriter.dll -a---- 15. 11. 2019 15:51 14712 System.Xml.Serialization.dll -a---- 15. 11. 2019 15:50 14712 System.Xml.XDocument.dll -a---- 15. 11. 2019 15:50 14712 System.Xml.XmlDocument.dll -a---- 15. 11. 2019 15:50 16760 System.Xml.XmlSerializer.dll -a---- 15. 11. 2019 15:50 14200 System.Xml.XPath.dll -a---- 15. 11. 2019 15:50 15736 System.Xml.XPath.XDocument.dll -a---- 20. 04. 2018 07:37 1016584 ucrtbase.dll -a---- 15. 11. 2019 15:51 398712 UIAutomationClient.dll -a---- 15. 11. 2019 15:51 863608 UIAutomationClientSideProviders.dll -a---- 15. 11. 2019 15:51 48504 UIAutomationProvider.dll -a---- 15. 11. 2019 15:51 277880 UIAutomationTypes.dll -a---- 1. 01. 1980 00:00 86504 vcruntime140_cor3.dll -a---- 15. 11. 2019 15:51 2211192 WindowsBase.dll -a---- 15. 11. 2019 15:51 204664 WindowsFormsIntegration.dll -a---- 1. 01. 1980 00:00 2150776 wpfgfx_cor3.dll |
Self-contained executable, single file
This options is exactly the same as previous one, except I have all files packed in one executable file. Just one executable and my .NET Core WPF app is working!
1 2 3 4 |
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7. 02. 2020 11:29 156485630 Jenx.NetCore3.PublishStrategies.exe -a---- 7. 02. 2020 11:29 1860 Jenx.NetCore3.PublishStrategies.pdb |
Self-contained executable, single file, ready-to-run
ReadyToRun option can improve the startup time of your .NET Core application. ReadyToRun is a form of ahead-of-time (AOT) compilation. ReadyToRun assemblies can (regarded to classical) contain IL (intermediate language) and native code. For example, ReadyToRun assembly compiled for .NET Core 3.0 and Linux x64 will only be usable on that specific configuration, because it contains native code that is only running on specific runtime environment.
ReadyToRun option is relative new options in the .NET Core. You can read more about this deployment option here: https://devblogs.microsoft.com/dotnet/announcing-net-core-3-0-preview-6.
1 2 3 4 |
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7. 02. 2020 11:29 156488198 Jenx.NetCore3.PublishStrategies.exe -a---- 7. 02. 2020 11:29 1860 Jenx.NetCore3.PublishStrategies.pdb |
In my experiment, I did not noticed any differences with this option enabled. But I only tested simple-empty WPF application. Probably, if application complexity would grow I expect I would noticed some better startup time and memory improvements.
But for now, I would use this option with care.
Self-contained executable, single file, trimmed.
The .NET Core 3.0 come with tool that can reduce the size of apps by analyzing IL and trims down unused code/assemblies. This means that compiled application is statically analyzed (at compile-time) and unused dependencies are removed from application.
This is my output:
1 2 3 4 |
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7. 02. 2020 11:31 90894180 Jenx.NetCore3.PublishStrategies.exe -a---- 7. 02. 2020 11:29 1860 Jenx.NetCore3.PublishStrategies.pdb |
As I noticed from my test, build & deploy process is very slow, because identifying and removing of unused dependencies from deploy is not an easy task. But at he end, deploy output is reduced quite a bit- in my case from approx. 150MB to 87MB (approx 42% size reduction). In some cases this is useful, but on the other hand it can be tricky and dangerous. If some runtime type-resolving features are used, e.g. reflection or some other runtime type-resolving mechanisms, trimming can be problematic. In this case application can behave very awkward. Again, trimming deployment option must be used carefully.
If you use reflection or you want IL linker not to exclude some assemblies from, you can explicitly say trimming tool to exclude specific assembly. You can do this by putting TrimmerRootAssembly
directive into your *.csproj file.
1 2 3 |
<ItemGroup> <TrimmerRootAssembly Include="MyAssembly.MyClass" /> </ItemGroup> |
This way IL linker will not trim out your assembly from deploy output.
Let’s take a look at Trimming and ReadyToRun options together.
Self-contained executable, single file, ready-to-run, trimmed.
This deploy is similar as previous one, except I put ReadyToRun option on. In this case, build time and the deploy size increased drastically. From 87MB to 136MB. It makes sense, because trimming reduced- removed some unused code, but on the other hand ReadyToRun option added some native code back in for performance purposes.
In this case, this is my WPF app deploy output:
1 2 3 4 |
Mode LastWriteTime Length Name ---- ------------- ------ ---- -a---- 7. 02. 2020 11:34 143063332 Jenx.NetCore3.PublishStrategies.exe -a---- 7. 02. 2020 11:31 1860 Jenx.NetCore3.PublishStrategies.pdb |
Conclusion
.NET Core 3.0+ introduced several new options for application deployment. Having more possibilities to deploy an application is generally a good thing, but it also brings problems if not used correctly.
I will really use this options (trimming and ready-to-run) with care. If I will have enough resources to deploy my app (e.g. enough disk, ability to preinstall runtime, etc…) I will stay on safe side :).
But in some scenarios where size matters -like small devices- trimming could be very handy mechanism to reduce application to a minimal possible size.
My advice: If you are not sure what this deployment options mean, It’s better to stay on safe side and use default deployment option – deploying runtime dependent applications.
If you want to experiment, you can pull source code from here: https://github.com/josipx/Jenx.NetCore3.PublishStrategies
One thought on “Deployment options in .NET Core 3.0”