In this post, LondonPilot discusses issues with EF Core migrations caused by .NET SDK version mismatches, seeking clarification on the roles of global.json and .csproj target frameworks.

Understanding the Need for global.json in .NET Projects

Context

LondonPilot describes an issue faced while maintaining a .NET project. Attempting to create an Entity Framework (EF) Core migration resulted in the error:

“The “DiscoverPrecompressedAssets” task failed unexpectedly. System.ArgumentException: An item with the same key has already been added.”

A solution found online largely resolved the problem. However, an additional step—installing the specific SDK version—was needed. Listing installed SDKs revealed only .NET 9 was present, even though the app targeted .NET 8.

Despite all .csproj files properly listing target frameworks, the app compiled and ran in debug mode with just .NET 9 installed. Running EF Core migrations, however, required:

  • Adding a global.json specifying the necessary .NET SDK version
  • Installing the corresponding SDK version

Key Questions

  1. Why is global.json needed for specifying the SDK version when target frameworks appear in .csproj?
  2. Why can the program compile and run without the relevant SDK, but advanced CLI tools (like EF Core migrations) fail unless it’s installed?

Explanation

Project File (.csproj) vs. global.json

  • The .csproj project file defines the target framework for your project (e.g., net8.0). This tells the compiler what runtime your code should run against and which APIs are available.
  • global.json allows you to fix the SDK version used by the CLI (e.g., dotnet build, dotnet ef). Without it, the CLI will use the latest installed SDK (here, .NET 9) that matches (or can interpret) your project.

Why Did it Work Without the Right SDK?

  • Many .NET SDKs are backward-compatible: the .NET 9 SDK can often build and debug apps targeting .NET 8, using reference assemblies for .NET 8.
  • However, tools like EF Core migrations sometimes rely on specific behaviors or features present only in a matching or older SDK. They may fail, or produce unexpected errors, if the SDK versions diverge.

Why Both Steps Were Needed

  • Adding global.json: Forces tools to use a specific SDK version (e.g., .NET 8) rather than a later SDK, ensuring command-line tools behave as expected.
  • Installing the SDK: If the SDK specified in global.json isn’t installed, commands will fail to run. Both the SDK and global.json are needed for reproducible builds and consistent dev team behavior.

Summary

For consistent CLI tool behavior—especially across teams or build servers—global.json pins the SDK version. Relying on just the latest installed SDK can lead to mysterious errors during advanced scenarios like EF Core migrations, even if most build/debug scenarios work.

Practical Recommendations

  • Commit a global.json with your desired SDK version to source control.
  • Ensure all contributors/installations have the SDKs in global.json.
  • Use dotnet --list-sdks to verify which SDKs are present and troubleshoot issues.

References

This post appeared first on “Reddit CSharp”. Read the entire article here