Solution files have been a part of the .NET and Visual Studio experience for many years now, and they’ve had the same custom format the whole time. Recently, the Visual Studio solution team has begun previewing a new, XML-based solution file format called SLNX. Starting in .NET SDK 9.0.200, the dotnet
CLI supports building and interacting with these files in the same way as it does with existing solution files. In the rest of this post we’ll show how users can migrate to the new format, explore the new support across the dotnet
CLI, and discuss the next steps towards a generally-available release of the format.
Getting started
Before the 9.0.200 SDK, the only way to create a SLNX file was through the Visual Studio settings. The Environment > Preview Features > Use Solution File Persistence Model
setting, when checked, would allow users to Save As
their existing .sln
files in the new .slnx
format.
The 9.0.200 SDK provides a command to do this same migration: dotnet sln migrate
.
Let’s start with a very simple solution and project setup to look at what it takes to migrate. First, we’ll create a new solution:
PS C:\Users\chethusk\Code\example> dotnet new sln
The template "Solution File" was created successfully.
PS C:\Users\chethusk\Code\example> cat .\example.sln
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Now, we’ll create a project and add it to the solution:
PS C:\Users\chethusk\Code\example> dotnet new console -n my-app
The template "Console App" was created successfully.
Processing post-creation actions...
Restoring C:\Users\chethusk\Code\example\my-app\my-app.csproj:
Restore succeeded.
PS C:\Users\chethusk\Code\example> dotnet sln add .\my-app\
Project `my-app\my-app.csproj` added to the solution.
PS C:\Users\chethusk\Code\example> cat .\example.sln
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 17
VisualStudioVersion = 17.0.31903.59
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "my-app", "my-app\my-app.csproj", "{845B7716-6F03-4D02-8E86-79F95485B5D7}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Debug|Any CPU.Build.0 = Debug|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Debug|x64.ActiveCfg = Debug|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Debug|x64.Build.0 = Debug|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Debug|x86.ActiveCfg = Debug|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Debug|x86.Build.0 = Debug|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Release|Any CPU.ActiveCfg = Release|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Release|Any CPU.Build.0 = Release|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Release|x64.ActiveCfg = Release|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Release|x64.Build.0 = Release|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Release|x86.ActiveCfg = Release|Any CPU
{845B7716-6F03-4D02-8E86-79F95485B5D7}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal
Now, let’s convert our solution to the new format:
PS C:\Users\chethusk\Code\example> dotnet sln migrate
.slnx file C:\Users\chethusk\Code\example\example.slnx generated.
PS C:\Users\chethusk\Code\example> cat .\example.slnx
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="my-app/my-app.csproj" />
</Solution>
The new format is XML-based and is much more concise than the old format – but it contains all of the same data! The data that is missing from the new format is part of the defaults of the format, so no functionality is lost.
This migration is made possible because the Visual Studio Solution team has created a new open-source library for parsing and working with both classic and XML-based solution files – the library is called Microsoft.VisualStudio.SolutionPersistence.
Managing projects from the CLI
You can do more than migrating solution files using the CLI, too. As you might expect, you can build the new solutions the same way you would build the old:
PS C:\Users\chethusk\Code\example> dotnet build .\example.slnx
Restore complete (0.6s)
my-app succeeded (4.3s) → my-app\bin\Debug\net9.0\my-app.dll
Build succeeded in 5.3s
Note
We specified the.slnx
file explicitly above because it’s an error to run dotnet build
or other commands that need to build in a directory with both a .sln
and a .slnx
– we don’t know which one to build!All of the other interactions you expect from the dotnet
CLI work as well. We can add projects:
PS C:\Users\chethusk\Code\example> dotnet new classlib -n my-lib
The template "Class Library" was created successfully.
Processing post-creation actions...
Restoring C:\Users\chethusk\Code\example\my-lib\my-lib.csproj:
Restore succeeded.
PS C:\Users\chethusk\Code\example> dotnet sln .\example.slnx add my-lib
Project `my-lib\my-lib.csproj` added to the solution.
PS C:\Users\chethusk\Code\example> cat .\example.slnx
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="my-app/my-app.csproj" />
<Project Path="my-lib/my-lib.csproj" />
</Solution>
We can list the projects in a solution:
PS C:\Users\chethusk\Code\example> dotnet sln .\example.slnx list
Project(s)
----------
my-app\my-app.csproj
my-lib\my-lib.csproj
And finally we can remove projects from the solution:
PS C:\Users\chethusk\Code\example> dotnet sln .\example.slnx remove .\my-lib\
Project `my-lib\my-lib.csproj` removed from the solution.
PS C:\Users\chethusk\Code\example> cat .\example.slnx
<Solution>
<Configurations>
<Platform Name="Any CPU" />
<Platform Name="x64" />
<Platform Name="x86" />
</Configurations>
<Project Path="my-app/my-app.csproj" />
</Solution>
Tip
There are two commands that don’t work in 9.0.200, though –dotnet nuget why
and dotnet list package
– those will begin working in the 9.0.201 release in March.Support for SLNX across .NET IDEs and Tooling
As mentioned above, the dotnet
CLI has broad support for the new SLNX file format, but there are still many tools in the ecosystem that have partial or no support for the format. You will need to take this varying level of support into account when choosing whether to migrate to SLNX files. Some examples of tools that have varying levels of support for slnx today are:
Visual Studio
While the IDE will read the SLNX file when loaded, it currently will not load SLNX files unless the setting to enable SLNX persistence has been enabled. This means if you work on a team and users have not toggled this setting, they will not be able to open SLNX files at all. In addition, double-clicking on SLNX files doesn’t currently open Visual Studio instances the way that sln files do.
C# Dev Kit
C# DevKit can support SLNX files, but in order to do so you must set the dotnet.defaultSolution
property to the path to your slnx file:
{
"dotnet.defaultSolution": "example.slnx"
}
slngen
The slngen tool is a command-line utility used to synthesize a solution file for a given project to help make repos that prefer not to use solution files interoperate better with Visual Studio. This tool is not yet updated to support SLNX – the status of this support can be tracked at microsoft/slngen#643.
JetBrains Rider
Rider has preliminary support for the SLNX format, and details about their support can be tracked at RIDER-110777.
Feedback and the road to GA
Despite this end-to-end support in Visual Studio and the dotnet
CLI, the SLNX format itself is still in preview. While we think it’s a great step forward in usability for many .NET developers, we want to hear from you as you try it in your teams. Try the migration paths in Visual Studio and the dotnet
CLI, make sure things work as you expect in your CI/CD pipelines and local builds, and make sure to let the teams know about your experiences in the following ways:
- for CLI experiences, report new issues or discussions at the dotnet/sdk repository
- for Visual Studio experiences, please raise new tickets at the Visual Studio Developer Community
- for feature requests for the solution parsing library, report new issues at the microsoft/vs-solutionpersistence repository
As we’re able to respond to your feedback and solidify core user experiences, we move closer towards being able to make this the default for Visual Studio and the dotnet
CLI.
Summary
SLNX files are an exciting new change to the solution file format that we think will make it easier for teams to collaborate and understand their projects. The new capabilities in the dotnet
CLI allow developers to have a full inner-loop and CI experience using the new format, so we’d love for .NET developers to read through the updated documentation, try the new support, and give us feedback!
Great improvement!
Please support globbing for at least the solution items (e.g. markdown and yaml files)
I saw at the Visual Studio Blog its a performance thing. But I think with globbing on load and with a reload button, is suitable for most use cases.
I thought the same. Would have made more sense to move project and solution files to json or yaml.
Microsoft’s reasoning of “It’s the same format as project files” is tail wagging the dog. They should have taken the opportunity to change both.
At least it’s better than the old format anyway! 🙏
Is this only a format change, or are there new features as well?
For example it would be great to be able to include global project config, like we do using Directory.Build.props and Directory.Build.targets currently.
Either way, its a great improvement.
For feature requests for the new format, please raise those over at the issue tracker for the new library.
How does this affect pipelines where many of the tasks point to sln files?
There should be no impact to your pipelines if you don’t migrate to the new slnx file format. This is an additive feature, not a replacement.
FYI I just tried our extension NDepend with VS 17.1.4.0 Preview2 and the interface Microsoft.VisualStudio.Shell.Interop.IVsSolutionPersistence that our extension is using to attach data to the solution, works as-is. Thank you :))
why still insist on XML? why not create a new standard using json or yaml?
The partner blog post on the Visual Studio blog has an answer for this, as well as more information about the requirements and need for the new format in general. This post is mostly about how the .NET CLI enables using the new format.
Nice work. Does it also support solution items? The old sln supported to include also non-projects files and allowed to manage these items like a folder structure.
Additionally, how do solution filters (slnf) work with the slnx?
Hi @Martin - the new format does support solution items and arbitrary solution folders - migration via VS or the `dotnet` CLI should preserve any of these that you already have. If this isn't working for you, please raise an issue at the vs-solutionpersistence repo.
Slnf files also work with the new format - you can point to either a .sln or a .slnx inside the .slnf file, and the list of projects in the slnf will be used as a filter over the 'full' view of the .sln or .slnx file. All .NET CLI commands that support slnf should support...
Any chance to backport it to sdk 8 or earlier?
Sorry Anton, that’s probably not going to happen at this time. It was quite a bit of work in the .NET CLI, and the feature isn’t yet stable in VS either. I would encourage you to use the .NET 9 SDK, even if you’re targeting .NET 8 runtimes, to make sure you get the latest tooling features regardless of what runtime(s) you choose to run your apps on.
The video mentions the .NET Framework; however, the article doesn’t seem to mention it. Will this work for the .NET Framework, or is it for .NET (core) only?
The new format will work with older, verbose project files and new, SDK-style project files – for both .NET Framework and .NET projects. So you should be covered regardless of the tech you use.
Is there the option to set the startup project(s)
Good question – I think you also mean “multiple startup projects”, right?. That would be mandatory for us too, if we want to migrate.
I don’t know the answer to this – I suggest you raise an issue at the vs-solutionpersistence repo to get feedback from the team that owns the new format about what the behavior should be, and if it’s supported or not. The .NET CLI doesn’t use this particular kind of metadata, so we haven’t had to stress-test that particular feature.
Yeah! Finally! I’ve been waiting for a revamp of solution files for years. It was one of the last remnants of an old project system, and didn’t match to the new SDK-style projects *at all*.
Just migrated one of my larger solutions, and it did so beautifully. Clean format, solution works in VS as before.
Makes sense to start out with the same features as the old format, but I’m looking forward to new capabilities. Especially possibilities to manage and finetune configurations, platforms, batch builds or deployments would be nice.
Great work!
Thanks for the kind words, and glad the migration has gone smoothly for you. Make sure you log all of those feature requests at the vs-solutionpersistence repo so the team can know what you’d like them to work on!