Skip to content
Draft
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Fix duplicate solution folder when adding multiple projects from same…
… directory

- Added check in GenerateIntermediateSolutionFoldersForProjectPath to reuse existing solution folder instead of creating duplicate
- Added test WhenMultipleProjectsFromSameDirectoryAreAddedSolutionFolderIsNotDuplicated to verify the fix
- All existing tests pass (214 passed, 2 skipped)

Co-authored-by: marcpopMSFT <[email protected]>
  • Loading branch information
Copilot and marcpopMSFT committed Dec 9, 2025
commit 22bf03f7227843f83ab0f930d6dd9693363139ac
13 changes: 10 additions & 3 deletions src/Cli/dotnet/Commands/Solution/Add/SolutionAddCommand.cs
Original file line number Diff line number Diff line change
Expand Up @@ -102,9 +102,16 @@ public override int Execute()
relativeSolutionFolderPath = _solutionFolderPath;
}

return string.IsNullOrEmpty(relativeSolutionFolderPath)
? null
: solution.AddFolder(GetSolutionFolderPathWithForwardSlashes(relativeSolutionFolderPath));
if (string.IsNullOrEmpty(relativeSolutionFolderPath))
{
return null;
}

// Check if a solution folder with this path already exists
var solutionFolderPath = GetSolutionFolderPathWithForwardSlashes(relativeSolutionFolderPath);
var existingFolder = solution.SolutionFolders.FirstOrDefault(f => f.Path == solutionFolderPath);

return existingFolder ?? solution.AddFolder(solutionFolderPath);
}

private async Task AddProjectsToSolutionAsync(IEnumerable<string> projectPaths, CancellationToken cancellationToken)
Expand Down
40 changes: 40 additions & 0 deletions test/dotnet.Tests/CommandTests/Solution/Add/GivenDotnetSlnAdd.cs
Original file line number Diff line number Diff line change
Expand Up @@ -409,6 +409,46 @@ public void WhenDirectoryContainsMultipleProjectsItCancelsWholeOperation(string
.Should().BeVisuallyEquivalentTo(contentBefore);
}

[Theory]
[InlineData("sln", ".sln")]
[InlineData("solution", ".sln")]
[InlineData("sln", ".slnx")]
[InlineData("solution", ".slnx")]
public async Task WhenMultipleProjectsFromSameDirectoryAreAddedSolutionFolderIsNotDuplicated(string solutionCommand, string solutionExtension)
{
var projectDirectory = _testAssetsManager
.CopyTestAsset("TestAppWithSlnAndCsprojFiles", identifier: $"GivenDotnetSlnAdd-{solutionCommand}{solutionExtension}")
.WithSource()
.Path;

var firstProject = Path.Combine("Multiple", "First.csproj");
var secondProject = Path.Combine("Multiple", "Second.csproj");
var cmd = new DotnetCommand(Log)
.WithWorkingDirectory(projectDirectory)
.Execute(solutionCommand, $"App{solutionExtension}", "add", firstProject, secondProject);
cmd.Should().Pass();

ISolutionSerializer serializer = SolutionSerializers.GetSerializerByMoniker(Path.Combine(projectDirectory, $"App{solutionExtension}"));
SolutionModel solution = await serializer.OpenAsync(Path.Combine(projectDirectory, $"App{solutionExtension}"), CancellationToken.None);

// The solution already has App project, plus we added First and Second = 3 total
var projectsInSolution = solution.SolutionProjects.ToList();
projectsInSolution.Count.Should().Be(3);
projectsInSolution.Should().Contain(p => p.FilePath.Contains("First.csproj"));
projectsInSolution.Should().Contain(p => p.FilePath.Contains("Second.csproj"));

// Should only have one solution folder for "Multiple", not two
var solutionFolders = solution.SolutionFolders.ToList();
solutionFolders.Count.Should().Be(1);
solutionFolders.Single().Path.Should().Contain("Multiple");

// Both new projects should be in the same solution folder
var solutionFolder = solutionFolders.Single();
var multipleProjects = projectsInSolution.Where(p => p.FilePath.Contains("Multiple")).ToList();
multipleProjects.Count.Should().Be(2);
multipleProjects.All(p => p.Parent?.Id == solutionFolder.Id).Should().BeTrue();
}

[Theory]
[InlineData("sln", ".sln")]
[InlineData("solution", ".sln")]
Expand Down
Loading