dotnet / dotnet/wpf

Incorrect SDK import order in temporary project file

Open
#11,150 0 comments 2 reactions 1 assignee Claimed by @dipeshmsft View on GitHub
Dominant language
C#
Stars
7.7k
Forks
1.3k
Avg merge
1d 11h
Merged PRs (30d)
61

Description

### Description

When a WPF project or its parent directory includes a Directory.Build.props file like the one below, the intermediate build process for WPF generates a random assembly name. As a result, local assembly types cannot be resolved, leading to an MC3050 error.

This issue has also been reported in #10068

### Reproduction Steps

The TestDirectoryProps.csproj file does not explicitly define an AssemblyName. Instead, Directory.Build.props dynamically sets the AssemblyName using $(MSBuildProjectName).

```xml


TestRootName

$(Product_RootNameSpace).$(MSBuildProjectName)

```

[Sample repository](https://github.com/gekka/TestWPFDirectoryProps)

### Expected behavior

The temporary assembly is referenced, and the type of the local assembly {x:Type} is resolved.

### Actual behavior

The build fails and no assembly is generated.

During the XAML-to-BAML conversion, {x:Type} resolution relies on the assembly name. Since the assembly name is random, the lookup fails, resulting in an MC3050 error.

### Regression?

_No response_

### Known Workarounds

As a workaround, setting the IncludePackageReferencesDuringMarkupCompilation property to false restores legacy behavior and prevents the error.
However, this breaks the functionality of source generators such as CSWin32.

### Impact

_No response_

### Configuration

.Net Farmework 4.7.2, .Net8 ,.Net 9

### Other information

#### Description of Behavior

1. In Microsoft.WinFx.targets, the GenerateTemporaryTargetAssembly target generates a randomly temporary project name if $(IncludePackageReferencesDuringMarkupCompilation) is not false.https://github.com/dotnet/wpf/blob/70126b959b5c9c8f0da61641fdbf43bac361bec3/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft.WinFX.targets#L373-L378
1. The GenerateTemporaryTargetAssembly task receives both the original assembly name (resolved via Directory.Build.props) and the randomly generated temporary project name.
https://github.com/dotnet/wpf/blob/70126b959b5c9c8f0da61641fdbf43bac361bec3/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft.WinFX.targets#L440-L458

1. This task generates a temporary project file, which includes the original assembly name.

1. This task start build of temporary project, but this building the temporary project file, Directory.Build.props is applied again.

1. At this point, the AssemblyName is override by the definition in Directory.Build.props, but $(MSBuildProjectName) now refers to the temporary project file name—resulting in a random assembly name.

1. A temporary assembly is generated with this random name.

1. The MarkupCompilePass2 target in Microsoft.WinFx.targets attempts to resolve local type names using this temporary assembly.

1. During the XAML-to-BAML conversion, {x:Type} resolution relies on the assembly name. Since the assembly name is random, the lookup fails, resulting in an MC3050 error.

#### Actual tempoary project file
```xml


TestRootName.TestDirectoryProps
obj\Debug\
obj\
S:\TestDirectoryProps\Test\TestDirectoryProps\obj\
<_TargetAssemblyProjectName>TestDirectoryProps
TestDirectoryProps



WinExe
net472;net8.0-windows;net9.0-windows
true

...
```

```cmd
msbuild /bl /t:_CompileTemporaryAssembly TestDirectoryProps_2rop0taq_wpftmp.csproj
```

As a result of building the temporary project file, an executable named after this temporary project file (e.g., TestDirectoryProps_2rop0taq_wpftmp.exe) is generated.

By inspecting the build log of the temporary project, you can confirm that the original project name in AssemblyName has been override by the temporary project name.

> Property reassignment: $(AssemblyName)="TestRootName.TestDirectoryProps_20otaqqp_wpftmp" (previous value: "TestRootName.TestDirectoryProps") at S:\TestDirectoryProps\Test\Directory.Build.props (6,3)

After identifying that the override was caused by Directory.Build.props, I investigated how it gets applied and found that it is imported transitively via Sdk.props.

The import order executed by Sdk.props is as follows, and it includes the import of Directory.Build.props.

```xml









```
This reveals that although the correct AssemblyName is defined within the temporary project file, it is later override by the definition from Directory.Build.props. In other words, an Import placed after the PropertyGroup ends up override the correct property with an incorrect one.

Therefore, the solution is to adjust the property evaluation order so that Directory.Build.props does not override the intended values.

#### Expected temporary project file
```xml


TestRootName.TestDirectoryProps
obj\Debug\
obj\
S:\TestDirectoryProps\Test\TestDirectoryProps\obj\
<_TargetAssemblyProjectName>TestDirectoryProps
TestDirectoryProps


WinExe
net472;net8.0-windows;net9.0-windows
true

...
```

After applying this fix, the build produced an assembly with the correct file name.

Image

I investigated why the temporary project file is generated in this particular order.

https://github.com/dotnet/wpf/blob/70126b959b5c9c8f0da61641fdbf43bac361bec3/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft/Build/Tasks/Windows/GenerateTemporaryTargetAssembly.cs#L267-L287

The method ReplaceImplicitImports called here inserts an element at the beginning of the project file.

https://github.com/dotnet/wpf/blob/70126b959b5c9c8f0da61641fdbf43bac361bec3/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft/Build/Tasks/Windows/GenerateTemporaryTargetAssembly.cs#L830-L840

Similarly, AddNewProperties inserts a element at the beginning of the project file.

https://github.com/dotnet/wpf/blob/70126b959b5c9c8f0da61641fdbf43bac361bec3/src/Microsoft.DotNet.Wpf/src/PresentationBuildTasks/Microsoft/Build/Tasks/Windows/GenerateTemporaryTargetAssembly.cs#L763-L764

Since AddNewProperties is executed after ReplaceImplicitImports, the element ends up appearing after .

#### Suggest for Fix:

To resolve this issue, the execution order should be modified so that ReplaceImplicitImports runs after AddNewProperties.

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.