Setup Local Preview .NET
09/01/2024
6 minutes
In this blog post I will detail how to setup early preview versions of .NET for development. My findings apply for .NET 9, but the methods should be applicable in general to most versions of .NET (considering version specific details).
While a preview version of .NET can be downloaded at any time from the dotnet website, installing the forthcoming preview version requires somewhat more investment.
The findings of this post are based on ASP.NET Core, which uses a similar, but more complex approach in its restore.cmd
command. This blog post will focus on the key components to have the latest SDK or a custom runtime available.
The preview versions of .NET can heavily change. Features may get added, removed, changed, or their performance might significantly improve. It is always suggested to choose and use an LTS or STS release for production applications. However, in certain cases a developer might want to test the latest preview features, in which case the methods described in this blog post will help.
Most of the scripts below are written in PowerShell
, which is cross-platform, when using the current version (v7.4.4). The key sections:
installing the SDK and runtime
override the default .NET SDK and runtime in a given project
setup Visual Studio to use the latest SDK or a custom runtime
setup nuget sources
Install the SDK and Runtime
The install script of .NET is available at https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1
. It can install both the SDK as well as the runtime. We can select to install different runtimes or SDKs.
The script below downloads the above installation script, then invokes it passing the versions: $sdkVersion = '9.0.100-preview.7.24371.4'
and $runtimeVersion = '9.0.0-rc.1.24412.13'
to install a custom SDK and runtime. To have .NET as a local installation for a given project, the script places all artifacts under the .dotnet
folder relative to the location of this script.
function GetDotNetInstallScript([string] $dotnetRoot) { $installScript = Join-Path $dotnetRoot 'dotnet-install.ps1' if (!(Test-Path $installScript)) { New-Item -Path $dotnetRoot -Force -ItemType 'Directory' | Out-Null $ProgressPreference = 'SilentlyContinue' $uri = "https://dotnet.microsoft.com/download/dotnet/scripts/v1/dotnet-install.ps1" Write-Host "GET $uri" Invoke-WebRequest $uri -OutFile $installScript } return $installScript } function InstallDotNet( [string] $dotnetRoot, [string] $version, [string] $runtime = '') { $dotnetVersionLabel = "'$version'" $installScript = GetDotNetInstallScript $dotnetRoot $installParameters = @{ Version = $version InstallDir = $dotnetRoot SkipNonVersionedFiles = $False NoPath = $True # PATH is not modified } if ($runtime -ne '' -and $runtime -ne 'sdk') { $installParameters.Runtime = $runtime } Write-Host "Attempting to install $dotnetVersionLabel" & $installScript @installParameters Write-Host "Successfully to installed $dotnetVersionLabel." } function UpdateGlobalJson( [string]$SdkVersion ) { $jsonContent = @{ sdk = @{ version = $SdkVersion } } | ConvertTo-Json -Depth 4 # Specify the output file path $outputFilePath = ".\global.json" # Write the JSON content to the file $jsonContent | Out-File -FilePath $outputFilePath -Encoding UTF8 Write-Host "global.json updated at $outputFilePath" } $sdkVersion = '9.0.100-preview.7.24371.4' $runtimeVersion = '9.0.0-rc.1.24412.13' $dotnetRoot = Join-Path $PSScriptRoot '.dotnet' InstallDotNet $dotnetRoot $sdkVersion 'sdk' InstallDotNet $dotnetRoot $runtimeVersion 'dotnet' UpdateGlobalJson $sdkVersion
To avoid all other projects using installation, the script is instructed not to change the PATH
by setting the NoPath
to true. Even after running this script all applications will still use the version of .NET that is installed on the machine by the regular installers.
Confirm this by running dotnet --info
, which will list the SDKs and runtimes installed on the machine, but does not list the downloaded ones:
.NET SDKs installed: 8.0.108 [C:\Program Files\dotnet\sdk] 9.0.100-preview.6.24328.19 [C:\Program Files\dotnet\sdk] 9.0.100-preview.7.24407.12 [C:\Program Files\dotnet\sdk]
Add an exclusion for the .dotnet
folder in the .gitignore
file, so that this folder is not added to the git repo:
.dotnet/
Enable the SDK
To enable using the downloaded runtime and SDK, a few environment variables must be set.
activate.ps1
This PowerShell script tests if it has been invoked dot sourced, so that the environment variables are set scope of the invoking session.
# This file must be invoked as ". .\activate.ps1" from the command line. if ($MyInvocation.CommandOrigin -eq 'runspace') { Write-Host -f Red "This script file must be 'dot sourced' by calling `". $PSCommandPath`"." exit 1 } # Tell dotnet where to find itself $env:DOTNET_ROOT = "$PSScriptRoot\.dotnet" $env:DOTNET_MULTILEVEL_LOOKUP = 0 $env:PATH = "${env:DOTNET_ROOT};${env:PATH}"
Invoke the above script dot sourced as: . .\activate.ps1
.
The script sets the .dotnet
folder - where the SDK and the runtime is installed - on the PATH for the current user session. It also disables multilevel lookup, so that the .NET tooling will not search for other SDK and runtime installations.
Executing dotnet --info
in the same session shows now the downloaded SDK and runtime:
.NET SDKs installed: 9.0.100-preview.7.24371.4 [D:\repos\<path>\.dotnet\sdk] ... .NET runtimes installed: Microsoft.NETCore.App 9.0.0-preview.7.24366.18 [D:\repos\<path>\.dotnet\shared\Microsoft.NETCore.App] Microsoft.NETCore.App 9.0.0-rc.1.24412.13 [D:\repos\<path>\.dotnet\shared\Microsoft.NETCore.App]
Executing the dotnet
command will now use this local .NET SDK.
startvs.cmd
To achieve the same with Visual Studio a cmd
can be created that also open the corresponding solution file:
@ECHO OFF
SETLOCAL
SET DOTNET_ROOT=%~dp0.dotnet
SET DOTNET_MULTILEVEL_LOOKUP=0
SET PATH=%DOTNET_ROOT%;%PATH%
start "" "ConsoleApp.sln"
It executes the same commands as the above powershell script, and then open the sln file.
Use the SDK and Runtime
It may happen that multiple SDKs or runtimes are installed in the .dotnet
folder. Notice that the install script does not remove previous installations. In such cases the .NET CLI will list all SDKs and runtimes installed in the .dotnet
folder. A project may control which one to be used by placing a global.json
file next to the project or solution file. The global.json
file tells which SDK to use for the given project. Notice that the installation script above create/overwrites this file with the currently installed version.
{ "sdk": { "version": "9.0.100-preview.7.24371.4" } }
A specific runtime (independent from the SDK) can be further set by the runtimeconfig.json
file or (<RollForward>latestminor</RollForward>
proeprty info member) or with the --fx-version
/ --roll-forward LatestMinor
when using the CLI.
Setup NuGet
Finally, when building and running the application in Visual Studio, additional nuget packages are required, for example: microsoft.net.sdk.compilers.toolset
. Certain versions of this package is available on nuget.org, but most version are only accessible from a private nuget source. To access these version, add a nuget.config
file to the repo enabling dotnet9 nuget sources:
<?xml version="1.0" encoding="utf-8"?> <configuration> <packageSources> <add key="dotnet9" value="https://pkgs.dev.azure.com/dnceng/public/_packaging/dotnet9/nuget/v3/index.json" /> </packageSources> </configuration>