If you have ever cracked open a .sln file and felt like you were reading ancient runes, welcome to the club. Solution GUIDs, nested GUIDs, configuration GUIDs. So many GUIDs I started hearing them in my sleep. With .NET 10, solution files finally learned to speak plain XML with .slnx. Fewer conflicts, cleaner diffs, and files you can actually read without summoning an archaeologist.
What changed in .NET 10
New solutions created with the .NET 10 SDK use .slnx by default. You can still opt into the old .sln when you need it, but the benefits are practical:
- Human readable XML that mirrors your folders
- Predictable diffs that keep Git happy
- Alignment with modern SDK-style project files
A tiny before and after
Here is a small two-project solution as .slnx. It reads like your Solution Explorer tree.
<Solution> <Folder Name="src"> <Project Path="src/Falcon.Api/Falcon.Api.csproj" /> <Project Path="src/Falcon.Core/Falcon.Core.csproj" /> </Folder></Solution>No project type GUIDs. No NestedProjects section. No configuration mapping per project. Just a straight list of what belongs where.
Create new solutions with .slnx
You do not have to do anything special. The default is already .slnx.
dotnet new sln -n MillenniumFalconNeed the legacy format for an old toolchain for a bit longer?
dotnet new sln -n MillenniumFalcon --format slnMigrate an existing solution in minutes
The CLI is the fastest route. Run this from the folder that contains your current .sln.
dotnet sln MyShip.sln migrateGot only one solution in the folder? You can let the CLI find it.
dotnet sln migrateWhat you get: a new MyShip.slnx beside your original .sln. Build and test with the new file, then delete the old one to avoid confusion.
Visual Studio path:
- File > Save Solution As
- Choose Xml Solution File (*.slnx)
Rider path:
- Right click solution > Save As .slnx
Tip: you need .NET SDK 9.0.200 or newer for the migrate command. Check your version with
dotnet --version.
Validate your build and tests
Your build commands do not change. Point them at the .slnx or let the CLI auto-discover the solution.
dotnet build --configuration ReleaseIf you like quick sanity checks, drop a tiny console app in your solution and make sure it still runs.
Before the snippet, a little context: verifying the runtime version is an easy smoke test that confirms you are building and running the same bits you expect.
using System;
Console.WriteLine($"Hello from .NET {Environment.Version}");And a tiny test also keeps the guardrails in place.
Quick thought: this test proves your test project builds and executes under the migrated solution.
using Xunit;
public class GandalfMath{ [Fact] public void YouShallNotDivideByZero() { Assert.Throws<DivideByZeroException>(() => { var _ = 42 / int.Parse("0"); }); }}Folders and nesting that match your mental model
Solution folders map to XML folders. What you see is what you get.
<Solution> <Folder Name="src"> <Folder Name="services"> <Project Path="src/services/Hogwarts.Api/Hogwarts.Api.csproj" /> </Folder> </Folder> <Folder Name="tests"> <Project Path="tests/Hogwarts.Tests/Hogwarts.Tests.csproj" /> </Folder></Solution>CI, Docker, and the boring stuff that should just work
If your pipeline calls dotnet restore, dotnet build, and dotnet test in the solution directory, it will keep working. The CLI discovers the solution file. The only time you need a change is when the pipeline hardcodes the file name. Update YourApp.sln to YourApp.slnx and you are done.
Dockerfile tip: copy the .slnx instead of the .sln if you cache restores at the solution level.
COPY ["YourApp.slnx", "."]RUN dotnet restoreWhy merge conflicts get less spicy
Picture two developers adding different projects on separate branches. With .sln, you get conflicting edits in three places: project list, configuration mappings, and nested projects. With .slnx, each added project is a single XML line under a folder. Git can usually merge those inserts cleanly. Less time wrestling with conflicts means more time writing code with names like DwightShruteBeetService.
Team rollout checklist
- Remove the old .sln after you validate the new file
- Update any hardcoded references in build scripts or Dockerfiles
- Verify SDK 9.0.200 or newer across all dev machines and runners
- Add a short note in your README about the switch
- If you have custom tools that parse .sln, check for updates or plan a short-term workaround
Wrap up
.slnx trades mystery for clarity. You get readable XML, fewer merge conflicts, simpler diffs, and a format that matches modern .NET. Migration is a one-liner, tooling support is broad, and your pipelines keep humming. The only real risk is hanging on to the old format and the merge headaches that come with it.
May your solution trees be tidy, your diffs be small, and your GUIDs stay in retirement.