Skip to main content

.NET Versioning

Introduction

Understanding .NET versioning is crucial for any .NET developer. As a beginner, you might find it confusing when people talk about .NET Framework 4.8, .NET Core 3.1, or .NET 5, 6, 7 and beyond. This guide will demystify .NET versioning and help you understand the various versions, how they relate to each other, and how versioning works in your projects.

.NET versioning encompasses several interrelated concepts:

  • Framework versions
  • Runtime versions
  • SDK versions
  • Target frameworks in project files
  • Package versions

By the end of this guide, you'll understand how these concepts work together and how to manage version dependencies in your .NET applications.

The Evolution of .NET

.NET Framework (2002-2019)

The original .NET Framework was Windows-only and evolved through numerous versions:

  • .NET Framework 1.0 (2002)
  • .NET Framework 2.0 (2005)
  • .NET Framework 3.0 & 3.5 (2006-2007)
  • .NET Framework 4.0 (2010)
  • .NET Framework 4.5-4.8 (2012-2019)

.NET Framework 4.8 is the final major version of the original .NET Framework.

.NET Core (2016-2019)

Microsoft introduced .NET Core as a cross-platform, open-source reimagining of .NET:

  • .NET Core 1.0 (2016)
  • .NET Core 2.0-2.2 (2017-2018)
  • .NET Core 3.0-3.1 (2019)

Modern .NET (2020-Present)

Starting with .NET 5, Microsoft unified the ecosystem:

  • .NET 5 (2020)
  • .NET 6 (2021) - LTS (Long Term Support) release
  • .NET 7 (2022)
  • .NET 8 (2023) - LTS release
  • .NET 9 (planned for 2024)

Understanding Target Frameworks

The Target Framework Moniker (TFM) is how you specify which version of .NET your project targets.

Common TFMs:

  • net48 - .NET Framework 4.8
  • netcoreapp3.1 - .NET Core 3.1
  • net5.0 - .NET 5
  • net6.0 - .NET 6
  • net7.0 - .NET 7
  • net8.0 - .NET 8

Here's how to specify a target framework in a project file:

xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
</Project>

Multi-targeting

You can target multiple frameworks in a single project:

xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net8.0</TargetFrameworks>
</PropertyGroup>
</Project>

This is especially useful for library projects that need to support multiple .NET versions.

SDK and Runtime Versions

SDK vs Runtime

  • SDK (Software Development Kit): Tools to build .NET applications
  • Runtime: Required components to run .NET applications

Checking Installed Versions

You can check what versions you have installed using:

bash
dotnet --list-sdks
dotnet --list-runtimes

Sample output:

.NET SDKs installed:
6.0.417 [C:\Program Files\dotnet\sdk]
7.0.306 [C:\Program Files\dotnet\sdk]
8.0.100 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
Microsoft.AspNetCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.AspNetCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
Microsoft.NETCore.App 6.0.25 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 7.0.14 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
Microsoft.NETCore.App 8.0.0 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]

Version Selection in Code

Runtime Checks

You can check the runtime version your application is using:

csharp
using System;

class Program
{
static void Main()
{
Console.WriteLine($".NET Version: {Environment.Version}");
Console.WriteLine($"Framework Description: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");
}
}

Sample output on .NET 8:

.NET Version: 8.0.0
Framework Description: .NET 8.0.0

Conditional Compilation

You can use conditional compilation to include code for specific versions:

csharp
public class VersionSpecificCode
{
public void DoWork()
{
#if NET48
// Code that runs only when targeting .NET Framework 4.8
Console.WriteLine("Running on .NET Framework 4.8");
#elif NET6_0_OR_GREATER
// Code that runs on .NET 6.0 or higher
Console.WriteLine("Running on .NET 6.0 or higher");
#else
// Fallback for other frameworks
Console.WriteLine("Running on another .NET version");
#endif
}
}

Package References and Version Ranges

When adding NuGet package references, you can specify version constraints:

xml
<ItemGroup>
<!-- Exact version -->
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />

<!-- Version range (greater than or equal to 6.0.0 but less than 7.0.0) -->
<PackageReference Include="Microsoft.Extensions.Logging" Version="6.0.0" />
</ItemGroup>

Common Versioning Patterns

  • 1.0.0 - Exact version
  • [1.0.0] - Exact version (alternate syntax)
  • (1.0.0,) - Greater than 1.0.0
  • [1.0.0,2.0.0) - Between 1.0.0 (inclusive) and 2.0.0 (exclusive)
  • * - Latest version

Real-World Example: Creating a Cross-Platform Library

Let's create a simple utility library that works across multiple .NET versions:

  1. First, create a multi-targeted project:
xml
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net48;net6.0;net8.0</TargetFrameworks>
</PropertyGroup>
</Project>
  1. Add version-specific code using conditional compilation:
csharp
using System;
using System.IO;

namespace CrossPlatformUtility
{
public static class FileHelper
{
public static string ReadTextFile(string path)
{
#if NET48
// .NET Framework approach
using (var reader = new StreamReader(path))
{
return reader.ReadToEnd();
}
#elif NET6_0_OR_GREATER
// Modern .NET approach
return File.ReadAllText(path);
#else
throw new PlatformNotSupportedException("Unsupported .NET version");
#endif
}

public static async Task<string> GetWebContentAsync(string url)
{
#if NET48
using (var client = new System.Net.WebClient())
{
return await client.DownloadStringTaskAsync(url);
}
#elif NET6_0_OR_GREATER
using var client = new System.Net.Http.HttpClient();
return await client.GetStringAsync(url);
#else
throw new PlatformNotSupportedException("Unsupported .NET version");
#endif
}
}
}
  1. Use the library in a console application:
csharp
using System;
using CrossPlatformUtility;

class Program
{
static async Task Main()
{
Console.WriteLine($"Running on: {System.Runtime.InteropServices.RuntimeInformation.FrameworkDescription}");

try
{
string content = FileHelper.ReadTextFile("sample.txt");
Console.WriteLine($"File content: {content}");

string webContent = await FileHelper.GetWebContentAsync("https://example.com");
Console.WriteLine($"Web content length: {webContent.Length} characters");
}
catch (Exception ex)
{
Console.WriteLine($"Error: {ex.Message}");
}
}
}

Managing .NET Versions in Development

global.json

You can control which SDK version is used for your project with a global.json file in your solution directory:

json
{
"sdk": {
"version": "8.0.100"
}
}

This ensures all developers use the same .NET SDK version, preventing unexpected behaviors due to SDK differences.

Using Multiple .NET Versions in Visual Studio

Visual Studio allows you to work with multiple .NET versions simultaneously. You can:

  1. Select target framework when creating new projects
  2. Change the target framework of existing projects
  3. Install side-by-side .NET versions

Summary

Understanding .NET versioning is essential for developing robust applications. In this guide, we've covered:

  • The evolution of .NET from .NET Framework to modern unified .NET
  • Target Framework Monikers (TFMs) and how to use them
  • The difference between SDK and Runtime versions
  • Conditional compilation for version-specific code
  • Package version constraints and management
  • Creating cross-platform libraries that work across .NET versions

By mastering these concepts, you'll be better equipped to build applications that work across different .NET environments and manage dependencies effectively.

Additional Resources

Exercises

  1. Create a new console application that targets .NET 8 and prints detailed runtime information.
  2. Create a class library that targets both .NET Framework 4.8 and .NET 6.0, implementing a method that handles file operations differently based on the framework.
  3. Add a package reference to your project with a version constraint that allows any 6.x version but not 7.0.
  4. Create a global.json file that specifies a specific .NET SDK version for your project.
  5. Write code that uses feature detection rather than version detection to adapt to different runtime capabilities.


If you spot any mistakes on this website, please let me know at [email protected]. I’d greatly appreciate your feedback! :)