Summary
A .slnx solution containing a project that imports a shared-items file (<Import Project="…\X.projitems" Label="Shared" />) is marked dirty by Visual Studio on every open. Saving — including File → Save Solution As — writes a byte-identical file (SHA-256 verified), and the save prompt returns on the next open, indefinitely.
This is consistent with VS's shared-items bookkeeping (persisted in classic .sln as GlobalSection(SharedMSBuildProjectFiles)) having no representation in the .slnx format: the in-memory model always differs from the parsed file, while the serializer has nothing to write for the difference. The .shproj itself need not be in the solution — an importing project alone triggers it.
Versions
- Visual Studio 2022 Community 17.14.37502.11
- Microsoft.VisualStudio.SolutionPersistence 1.0.52 (NuGet latest, and the identical copy VS ships in
PrivateAssemblies, FileVersion 1.0.52.6595)
Minimal repro (4 files)
Repro.slnx
<Solution>
<Project Path="Lib/Lib.csproj" />
</Solution>
Lib/Lib.csproj
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>netstandard2.0</TargetFramework>
</PropertyGroup>
<Import Project="..\Shared\Shared.projitems" Label="Shared" />
</Project>
Shared/Shared.projitems and Shared/SharedClass.cs (full contents)
Shared/Shared.projitems
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<PropertyGroup>
<MSBuildAllProjects Condition="'$(MSBuildVersion)' == '' Or '$(MSBuildVersion)' < '16.0'">$(MSBuildAllProjects);$(MSBuildThisFileFullPath)</MSBuildAllProjects>
<HasSharedItems>true</HasSharedItems>
<SharedGUID>a1b2c3d4-0000-4000-8000-000000000001</SharedGUID>
</PropertyGroup>
<PropertyGroup Label="Configuration">
<Import_RootNamespace>Shared</Import_RootNamespace>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)SharedClass.cs" />
</ItemGroup>
</Project>
Shared/SharedClass.cs
namespace Shared
{
internal static class SharedClass
{
public static int Answer => 42;
}
}
Steps: open Repro.slnx in VS → let it load → close the solution → VS prompts to save Repro.slnx. Accept: the file is rewritten with identical bytes. Reopen: same prompt. (Confirmed against exactly these files.)
Evidence the on-disk file is already canonical
- A load→save round-trip through SolutionPersistence 1.0.52 reproduces both this file and a 654-project real-world
.slnx byte-for-byte.
- VS Save Solution As on the dirty solution produces a SHA-256-identical file.
- Bisected across ten probe solutions: a
<Configurations> block, solution folders with and without explicit Ids, and self-closing intermediate folders all open clean; the only trigger found is a project importing a .projitems. Removing only the Label="Shared" import line from an otherwise-identical project makes the solution open clean.
- For contrast, classic
.sln persists this bookkeeping (SharedMSBuildProjectFiles), so after one save such a solution reopens clean.
Expected
Either the format/model round-trips shared-items bookkeeping (an element analogous to SharedMSBuildProjectFiles), or VS does not flag the solution dirty for state the serializer will never persist.
Context
Found in ritchiecarroll/go2cs, where both solutions contain two projects importing a shared Symbols project — every open prompts. Happy to provide more detail or test candidate fixes.
Summary
A
.slnxsolution containing a project that imports a shared-items file (<Import Project="…\X.projitems" Label="Shared" />) is marked dirty by Visual Studio on every open. Saving — including File → Save Solution As — writes a byte-identical file (SHA-256 verified), and the save prompt returns on the next open, indefinitely.This is consistent with VS's shared-items bookkeeping (persisted in classic
.slnasGlobalSection(SharedMSBuildProjectFiles)) having no representation in the.slnxformat: the in-memory model always differs from the parsed file, while the serializer has nothing to write for the difference. The.shprojitself need not be in the solution — an importing project alone triggers it.Versions
PrivateAssemblies, FileVersion 1.0.52.6595)Minimal repro (4 files)
Repro.slnxLib/Lib.csprojShared/Shared.projitemsandShared/SharedClass.cs(full contents)Shared/Shared.projitemsShared/SharedClass.csSteps: open
Repro.slnxin VS → let it load → close the solution → VS prompts to saveRepro.slnx. Accept: the file is rewritten with identical bytes. Reopen: same prompt. (Confirmed against exactly these files.)Evidence the on-disk file is already canonical
.slnxbyte-for-byte.<Configurations>block, solution folders with and without explicitIds, and self-closing intermediate folders all open clean; the only trigger found is a project importing a.projitems. Removing only theLabel="Shared"import line from an otherwise-identical project makes the solution open clean..slnpersists this bookkeeping (SharedMSBuildProjectFiles), so after one save such a solution reopens clean.Expected
Either the format/model round-trips shared-items bookkeeping (an element analogous to
SharedMSBuildProjectFiles), or VS does not flag the solution dirty for state the serializer will never persist.Context
Found in ritchiecarroll/go2cs, where both solutions contain two projects importing a shared Symbols project — every open prompts. Happy to provide more detail or test candidate fixes.