Upgrade .NET projects to newer versions.
net6.0 → net9.0)List all project files that need upgrading:
find . -name "*.csproj" -o -name "*.vbproj" -o -name "*.fsproj"
Open each project file (.csproj, .vbproj, or .fsproj) and update the <TargetFramework> property:
Before:
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
After (upgrading to .NET 9):
<PropertyGroup>
<TargetFramework>net9.0</TargetFramework>
</PropertyGroup>
For multi-targeted projects, update <TargetFrameworks>:
<PropertyGroup>
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>
</PropertyGroup>
If using specialized workloads (MAUI, ASP.NET, etc.):
dotnet workload restore
dotnet build
dotnet test
The SDK will provide warnings and errors to guide further changes.
Create or update global.json in your solution root to control which SDK version is used:
dotnet new globaljson --sdk-version 9.0.100 --roll-forward latestFeature
This ensures consistent builds across different machines and CI environments.
File structure after:
solution-root/
├── global.json (pins SDK version)
├── src/
│ ├── Project1/
│ └── Project2/
└── tests/
Lock analyzer rules to a specific .NET version to prevent new warnings on upgrade:
<PropertyGroup>
<AnalysisLevel>9.0</AnalysisLevel>
</PropertyGroup>
This is useful when upgrading gradually—prevents new analyzer rules from breaking builds before you're ready to address them.
Create Directory.Packages.props in solution root:
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Microsoft.Extensions.AI" Version="9.10.1" />
<PackageVersion Include="Azure.Identity" Version="1.17.0" />
</ItemGroup>
</Project>
In project files, reference without version:
<ItemGroup>
<PackageReference Include="Microsoft.Extensions.AI" />
<PackageReference Include="Azure.Identity" />
</ItemGroup>
Enable lock files to ensure reproducible restores:
<PropertyGroup>
<RestorePackagesWithLockFile>true</RestorePackagesWithLockFile>
<RestoreLockedMode>true</RestoreLockedMode>
</PropertyGroup>
Generate lock file:
dotnet restore
Update your CI/CD pipeline to use the new SDK version. For GitHub Actions:
Before:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '8.0'
After:
- name: Setup .NET
uses: actions/setup-dotnet@v4
with:
dotnet-version: '9.0'
Copy this checklist and track progress:
Upgrade Progress:
- [ ] Review breaking changes for target .NET version
- [ ] Update all .csproj files to new target framework
- [ ] Run dotnet workload restore if needed
- [ ] Build entire solution: dotnet build
- [ ] Run full test suite: dotnet test
- [ ] Update global.json if using version pinning
- [ ] Update CI/CD pipeline configuration
- [ ] Test in CI/CD environment
- [ ] Commit changes
Check for breaking changes specific to your target version:
Update Dockerfile FROM statements:
# Old
FROM mcr.microsoft.com/dotnet/aspnet:8.0
# New
FROM mcr.microsoft.com/dotnet/aspnet:9.0
Configuration changes are required through Azure Portal or IaC (e.g., Bicep, Terraform).
Install new .NET runtime via package manager:
# Ubuntu/Debian
sudo apt-get update
sudo apt-get install -y dotnet-runtime-9.0
dotnet builddotnet testdotnet clean and rebuildglobal.json is in repo root (CI should respect it)For a single .csproj project:
<TargetFramework>net9.0</TargetFramework>dotnet builddotnet testFor solutions with multiple projects and GitHub Actions:
.csproj files in the solutionglobal.json in solution root.github/workflows/*.yml to use new versiondotnet testFor libraries supporting multiple .NET versions:
<TargetFrameworks>net8.0;net9.0</TargetFrameworks>dotnet test --framework net8.0 && dotnet test --framework net9.0