Skip to content

Conversation

marcpopMSFT
Copy link
Member

Reverts #49328

Based on data, 300k out of 5M projects have a dash but only 6k don't have root namespace. This potentially breaks resource scenarios as identified by the runtime team. So the scope of people fixed is probably not worth taking the change versus the risk of breaking customers. Those who use template scenarios can fix their bad namespaces manually as they have been this whole time.

CC @jkotas

@Copilot Copilot AI review requested due to automatic review settings August 26, 2025 20:28
Copy link
Contributor

This PR is targeting main, which is now for .NET 11-facing work. If you intended to target .NET 10, either retarget this PR to release/10.0.1xx or make sure you backport the change to release/10.0.1xx after merging. See #50394 for more details.

Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR reverts changes that made RootNamespace handling safer for project names with dashes and starting digits. The revert is based on data showing that while 300k out of 5M projects have dashes, only 6k don't have root namespace, and the change poses risks to resource scenarios that outweigh the benefits.

  • Removes enhanced RootNamespace transformation logic that handled dashes and digit prefixes
  • Removes comprehensive test coverage for the RootNamespace handling scenarios
  • Restores simpler RootNamespace logic that only replaces spaces with underscores

Reviewed Changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.

File Description
src/Tasks/Microsoft.NET.Build.Tasks/targets/Microsoft.NET.Sdk.props Reverts RootNamespace property logic to only handle spaces, removing dash and digit handling
test/Microsoft.NET.Build.Tests/GivenThatWeWantToBuildALibrary.cs Removes test methods and helper function for RootNamespace handling scenarios

Comment on lines +1053 to +1071
string GetPropertyValue(string propertyName)
{
Name = "13monkeys",
TargetFrameworks = targetFramework,
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework);

// Overwrite the default file. CreateTestProject uses the defined project name for the namespace.
// We need a buildable project to extract the property to verify it
// since this issue only surfaces in VS when adding a new class through an item template.
File.WriteAllText(Path.Combine(testAsset.Path, testProject.Name, $"{testProject.Name}.cs"), @"
using System;
using System.Collections.Generic;

namespace _13monkeys
{
public class _13monkeysClass
{
public static string Name { get { return ""13monkeys""; } }
public static List<string> List { get { return null; } }
}
}");
string projectFolder = Path.Combine(testAsset.Path, testProject.Name);

var buildCommand = new BuildCommand(testAsset, $"{testProject.Name}");
buildCommand
.Execute()
.Should()
.Pass();

GetPropertyValue("RootNamespace", projectFolder, testProject.TargetFrameworks).Should().Be("_13monkeys");
}

[Theory]
[InlineData("netcoreapp3.1")]
[InlineData("netcoreapp5.0")]
public void It_makes_RootNamespace_safe_when_project_name_has_dashes_and_starts_with_digit(string targetFramework)
{
var testProject = new TestProject()
{
Name = "13-monkeys-project",
TargetFrameworks = targetFramework,
};

var testAsset = _testAssetsManager.CreateTestProject(testProject, identifier: targetFramework);

// Overwrite the default file. CreateTestProject uses the defined project name for the namespace.
// We need a buildable project to extract the property to verify it
// since this issue only surfaces in VS when adding a new class through an item template.
File.WriteAllText(Path.Combine(testAsset.Path, testProject.Name, $"{testProject.Name}.cs"), @"
using System;
using System.Collections.Generic;
var getValuesCommand = new GetValuesCommand(Log, projectFolder,
testProject.TargetFrameworks, propertyName, GetValuesCommand.ValueType.Property)
{
Configuration = "Debug"
};

namespace _13_monkeys_project
{
public class _13_monkeys_projectClass
{
public static string Name { get { return ""13-monkeys-project""; } }
public static List<string> List { get { return null; } }
}
}");
string projectFolder = Path.Combine(testAsset.Path, testProject.Name);
getValuesCommand
.Execute()
.Should()
.Pass();

var buildCommand = new BuildCommand(testAsset, $"{testProject.Name}");
buildCommand
.Execute()
.Should()
.Pass();
var values = getValuesCommand.GetValues();
values.Count.Should().Be(1);
return values[0];
}

GetPropertyValue("RootNamespace", projectFolder, testProject.TargetFrameworks).Should().Be("_13_monkeys_project");
GetPropertyValue("RootNamespace").Should().Be("Project_Name_With_Spaces");
Copy link
Preview

Copilot AI Aug 26, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[nitpick] The GetPropertyValue method is now a local function but duplicates logic that was previously in a private method. Consider whether this local function approach provides sufficient reusability if similar property extraction is needed elsewhere in the test class.

Copilot uses AI. Check for mistakes.

Copy link
Member

@baronfel baronfel left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM. We're gonna backport this, right?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants