Mysterious name resolution differences between legacy and SDK-style C# projects
Asked Answered
N

1

3

I encountered this confusing names-resolution problem while trying to convert legacy solution with .NET 4.6.1 projects to the new SDK-style project formats. I was able to create a minimal repro solution LegacyVsSdkStyleProjectNameResolution, which is on GitHub here:

https://github.com/PaloMraz/LegacyVsSdkStyleProjectNameResolution.git

The solution contains:

  • 3 legacy projects targeting .NET Framework 4.6.1 residing in the „Legacy“ solution folder; the project names start with „Legacy.“).
  • 3 SDK-style projects targeting .NET residing in the „Sdk“ solution folder; the project names start with „Sdk.“.

(Please forgive me the non-intuitive assembly / namespace / class names – I could not use the original names and was not able to devise something more feasible).

The dependencies are shown in this image:

CodeMap

The problem is, the Legacy.Common.Configuration project successfuly compiles with this Bundle class declaration:

using Legacy.Common.Data;

namespace Legacy.Common.Configuration
{
  public class Bundle
  {
    public Person Person { get; set; }
  }
}

But the Sdk.Common.Configuration produces compilation error CS0118 ('Person' is a namespace but is used like a type):

using Sdk.Common.Data;

namespace Sdk.Common.Configuration
{
  public class Bundle
  {
    public Person Person { get; set; }
  }
}

Can anybody please explain, why the C# compiler in the Sdk.Common.Configuration project resolves the Person type symbol incorrectly to namespace Sdk.Common.Person, but in the Legacy.Common.Configuration it resolves it correctly to Legacy.Common.Data.Person class?

Nomo answered 20/5, 2022 at 11:39 Comment(3)
Nice question: good mcve, good downloadable repro. Very impressed, +1Pettaway
i'm curios what was the tool used for this drawing please ? excellentChaise
The drwaing is the standard Visual Studio Code Map tooling - learn.microsoft.com/en-us/visualstudio/modeling/…Nomo
P
2

In SDK-style projects, a project will automatically inherit all of the things that its dependencies depend on. In legacy projects this had to be done manually, with the tooling inserting explicit references to all of the things that a new dependency depended on.

So in your SDK-style project, Sdk.Common.Configuration is inheriting a reference to Sdk.Common.Person.Management, by virtue of the fact that it references Sdk.Common.Data.


Sdk.Common.Person.Management defines the namespace Sdk.Common.Person:

namespace Sdk.Common.Person.Management
{
  ...
}

While Sdk.Common.Data defines the class Sdk.Common.Data.Person.:

namespace Sdk.Common.Data
{
  public class Person
  {
  }
}

Sdk.Common.Configuration then does this:

using Sdk.Common.Data;

namespace Sdk.Common.Configuration
{
  public class Bundle
  {
    public Person Person { get; set; }
  }
}

There, the compiler searches the namespace Sdk.Common.Configuration (and Sdk.Common, and Sdk) for Person before it considers that using. It finds Sdk.Common.Person which is a namespace, and complains because you're using a namespace as a type.

If you include the using inside the namespace, this order flips, and the compiler will search Sdk.Common.Data first, and find the Sdk.Common.Data.Person class:

namespace Sdk.Common.Configuration
{
  using Sdk.Common.Data;

  public class Bundle
  {
    public Person Person { get; set; }
  }
}

See this answer for details.


As for how to solve this, first off don't define a class and a namespace with the same name. If you can't avoid this, you can either change the location of the using as shown above, or use the fully-qualified name of the Person class.

If you want to mirror the legacy behaviour of Sdk.Common.Data's references not being inherited by Sdk.Common.Configuration, use PrivateAsserts="All" on the ProjectReference which includes Sdk.Common.Person.Management.

<ProjectReference Include="..\Sdk.Common.Person.Management\Sdk.Common.Person.Management.csproj" PrivateAssets="All" />

This states that Sdk.Common.Person.Management is a private dependency, which isn't inherited by anything which references Sdk.Common.Data.

Pettaway answered 20/5, 2022 at 12:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.