Skip to content

TaskHostFactory does not work with public properties with private get accessor as task parameters #116012

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
PetSerAl opened this issue May 25, 2025 · 6 comments · May be fixed by #116080
Open
Assignees
Milestone

Comments

@PetSerAl
Copy link

Issue Description

TaskHostFactory does not allow using public properties with private get accessor as task parameters.

Steps to Reproduce

A/A.cs

using Microsoft.Build.Utilities;

public sealed class SampleTask : Task
{
	public string? S1 { private get; set; }
	public string? S2 { get; set; }
	public string? S3 { set { } }

	public SampleTask() { }

	public override bool Execute() => true;
}

A/A.csproj

<Project Sdk="Microsoft.NET.Sdk">
	<PropertyGroup>
		<Nullable>Enable</Nullable>
		<TargetFramework>net9.0</TargetFramework>
		<OutDir>../out</OutDir>
	</PropertyGroup>
	<ItemGroup>
		<PackageReference Include="Microsoft.Build.Tasks.Core">
			<Version>17.14.8</Version>
		</PackageReference>
	</ItemGroup>
</Project>

B/B.csproj

<Project Sdk="Microsoft.NET.Sdk">
	<PropertyGroup>
		<Nullable>Enable</Nullable>
		<TargetFramework>net9.0</TargetFramework>
	</PropertyGroup>
	<UsingTask TaskName="SampleTask" TaskFactory="TaskHostFactory" AssemblyFile="../out/A.dll" Condition="$(UseTaskHost)=='True'" />
	<UsingTask TaskName="SampleTask" AssemblyFile="../out/A.dll" Condition="$(UseTaskHost)=='False'" />
	<Target Name="TestTarget" AfterTargets="Build">
		<SampleTask S1="Value" Condition="$(TestParameter)=='S1'" />
		<SampleTask S2="Value" Condition="$(TestParameter)=='S2'" />
		<SampleTask S3="Value" Condition="$(TestParameter)=='S3'" />
	</Target>
</Project>

Test.cmd

dotnet build A
dotnet build B -p:UseTaskHost=False -p:TestParameter=S1
dotnet build B -p:UseTaskHost=False -p:TestParameter=S2
dotnet build B -p:UseTaskHost=False -p:TestParameter=S3
dotnet build B -p:UseTaskHost=True -p:TestParameter=S1
dotnet build B -p:UseTaskHost=True -p:TestParameter=S2
dotnet build B -p:UseTaskHost=True -p:TestParameter=S3

Test.zip
Run Test.cmd from zip file.

Expected Behavior

All calls to dotnet build should be successful.

Actual Behavior

Call to dotnet build B -p:UseTaskHost=True -p:TestParameter=S1 fails with

Restore complete (0,7s)
  B failed with 2 error(s) (0,5s) → B\bin\Debug\net9.0\B.dll
    M:\Temp\Test\B\B.csproj(9,15): error MSB4064: The "S1" parameter is not supported by the "SampleTask" task loaded from assembly: A, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null from the path: M:\Temp\Test\out\A.dll. Verify that the parameter exists on the task, the <UsingTask> points to the correct assembly, and it is a settable public instance property.
    M:\Temp\Test\B\B.csproj(9,3): error MSB4063: The "SampleTask" task could not be initialized with its input parameters.

Build failed with 2 error(s) in 2,4s

Analysis

https://github.com/dotnet/msbuild/blob/4ad462496537cd497f9c43531acb21f44d58cd67/src/Shared/LoadedType.cs#L70
When assembly loaded via MetadataLoadContext, then call to type.GetProperties(BindingFlags.Instance | BindingFlags.Public); will not return public properties with private get accessor. But when assembly loaded for execution, then such properties will be included.

Versions & Configurations

.NET SDK:
 Version:           9.0.300
 Commit:            15606fe0a8
 Workload version:  9.0.300-manifests.c678e91b
 MSBuild version:   17.14.5+edd3bbf37

Runtime Environment:
 OS Name:     Windows
 OS Version:  10.0.26100
 OS Platform: Windows
 RID:         win-x64
 Base Path:   C:\Program Files\dotnet\sdk\9.0.300\

Host:
  Version:      9.0.5
  Architecture: x64
  Commit:       e36e4d1a8f

.NET SDKs installed:
  9.0.300 [C:\Program Files\dotnet\sdk]

.NET runtimes installed:
  Microsoft.AspNetCore.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.AspNetCore.App]
  Microsoft.NETCore.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.NETCore.App]
  Microsoft.WindowsDesktop.App 9.0.5 [C:\Program Files\dotnet\shared\Microsoft.WindowsDesktop.App]
@KalleOlaviNiemitalo
Copy link

When assembly loaded via MetadataLoadContext, then call to type.GetProperties(BindingFlags.Instance | BindingFlags.Public); will not return public properties with private get accessor. But when assembly loaded for execution, then such properties will be included.

I think that is a .NET Runtime bug that such a difference exists.

Reflection of a property from MetadataLoadContext uses the visibility of the get accessor if that exists, otherwise the set accessor: https://github.com/dotnet/runtime/blob/cf5369440e236aa783599761750b4f8e1f4aa719/src/libraries/System.Reflection.MetadataLoadContext/src/System/Reflection/Runtime/BindingFlagSupport/PropertyPolicies.cs

Reflection of a runtime property instead computes Attributes.ComposedOfAllPrivateMethods and Attributes.ComposedOfNoPublicMembers flags: https://github.com/dotnet/runtime/blob/cf5369440e236aa783599761750b4f8e1f4aa719/src/coreclr/System.Private.CoreLib/src/System/Reflection/Associates.cs

@KalleOlaviNiemitalo
Copy link

I did not find any dotnet/runtime issue for this.

@MichalPavlik
Copy link
Member

Triage: We should transfer this issue to runtime team.

@JanProvaznik JanProvaznik transferred this issue from dotnet/msbuild May 27, 2025
@dotnet-policy-service dotnet-policy-service bot added the untriaged New issue has not been triaged by the area owner label May 27, 2025
@github-actions github-actions bot added the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label May 27, 2025
Copy link
Contributor

Tagging subscribers to this area: @vitek-karas, @agocke, @VSadov
See info in area-owners.md if you want to be subscribed.

Copy link
Contributor

Tagging subscribers to this area: @dotnet/area-system-reflection
See info in area-owners.md if you want to be subscribed.

@vcsjones vcsjones removed the needs-area-label An area label is needed to ensure this gets routed to the appropriate area owners label May 28, 2025
@ericstj
Copy link
Member

ericstj commented May 28, 2025

So it sounds like we should make PropertyPolicies consider both the Get and Set method when calculating GetMemberAttributes for a PropertyInfo. That way it can correctly report the property as visible if it has a public setter but internal or private getter.

@ericstj ericstj removed the untriaged New issue has not been triaged by the area owner label May 28, 2025
@ericstj ericstj added this to the Future milestone May 28, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: No status
Development

Successfully merging a pull request may close this issue.

7 participants