Die benutzerdefinierte MSBuild C#-Aufgabe löst eine MissingMethodException ausC#

Ein Treffpunkt für C#-Programmierer
Anonymous
 Die benutzerdefinierte MSBuild C#-Aufgabe löst eine MissingMethodException aus

Post by Anonymous »

Ich habe eine sehr seltsame Situation. Derzeit schreibe ich eine benutzerdefinierte MSBuild-Aufgabe in C#, die ich in meinem Build-Prozess verwenden möchte. Diese Aufgabe verweist auf ein NuGet-Paket, das die HttpClient-Erweiterungsmethode Task GetFromJsonAsync(this HttpClient client, string? requestUri, JsonTypeInfo jsonTypeInfo, CancellationToken cancellingToken) aufruft, die in der System.Net.Http.Json-Assembly implementiert ist. Meine eigene Aufgabe ist auf .NET10 ausgerichtet und das NuGet-Paket ist auf .NET9 ausgerichtet.
Ich habe die Aufgabe auf folgende Weise zu meinem Build hinzugefügt:

Code: Select all




Wenn ich die Aufgabe während des Builds ausführe, erhalte ich die folgende Ausnahme:

Code: Select all

error MSB4018: System.MissingMethodException->
Microsoft.Build.Framework
.BuildException
.GenericBuildTransferredException:
Method not found: 'System.Threading.Tasks.Task`1
System.Net.Http
.Json
.HttpClientJsonExtensions
.GetFromJsonAsync(System.Net.Http.HttpClient,
System.String,
System.Text.Json.Serialization.Metadata.JsonTypeInfo`1,
System.Threading.CancellationToken)'.

Wenn ich jetzt eine Konsolenanwendung erstelle, die mit meiner benutzerdefinierten Aufgabe auf die Assembly verweist, und dann die Aufgabe ausführe, verwende ich den folgenden Code:

Code: Select all

MyTask task = new MyTask();
task.BuildEngine = new BuildEngine();
task.Execute();
Die Aufgabe ist ohne Laufzeitausnahmen erfolgreich.
Um die Ursache dafür zu finden, habe ich Folgendes versucht und untersucht:
  • Klonen Sie das NuGet-Paketquell-Repository und ändern Sie die Projekte so, dass sie auf .NET10 statt auf .NET9 abzielen, und verwenden Sie meinen benutzerdefinierten NuGet-Paketbuild. Dadurch wurde das Problem nicht behoben und die MissingMethodException wird immer noch ausgelöst.
  • Führen Sie den Prozessmonitor von sysinternals aus, um zu sehen, ob welche DLLs während der Ausführung der Aufgabe während eines Builds geladen werden. Ich habe gesehen, dass die Datei C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll korrekt geladen wurde, und habe diese Datei in ILSpy geladen, um zu sehen, ob die Methode tatsächlich in dieser Assembly vorhanden ist, und das ist der Fall.
  • Verglichen die Signatur der GetFromJsonAsync-Methode in .NET9 und .NET10 und es scheint nicht so zu sein Es kann einen Unterschied in der Signatur geben, sogar das Parameterattribut für den Parameter requestUri ist dasselbe.
  • Es wurde ein Ereignishandler zu AppDomain.CurrentDomain.AssemblyLoad hinzugefügt, der nur protokolliert, welche Assembly geladen wird, und die Assembly wird als vorhanden protokolliert geladen (bestätigt, was Process Monitor bereits angegeben hat).
  • Sehen Sie sich die IL der Methode im NuGet-Paket an, um zu sehen, welche Methode tatsächlich aufgerufen wird: call class [System.Runtime]System.Threading.Tasks.Task`1 [System.Net.Http.Json]System.Net.Http.Json.HttpClientJsonExtensions::GetFromJsonAsync(class [System.Net.Http]System.Net.Http.HttpClient, string, class [System.Text.Json]System.Text.Json.Serialization.Metadata.JsonTypeInfo`1, valuetype [System.Runtime]System.Threading.CancellationToken). Es scheint mir, dass die Methode wie erwartet aus der System.Net.Http.Json-Assembly aufgerufen werden soll.
Ich verwende Visual Studio Version 18.1.1 (neueste Version) und erstelle aus der IDE, aber es schlägt auch fehl, wenn der Build mit MSBuild in der Befehlszeile ausgeführt wird. Die von MSBuild gemeldete Version ist:

Code: Select all

MSBuild version 18.0.5+e22287bf1 for .NET Framework
18.0.5.56406
Kann ich irgendetwas ausprobieren? Was übersehe ich hier?
UPDATE
Mit dotnet-trace Ich hoffe, dass ich ein paar zusätzliche Informationen habe. Ich habe das Tool mit der folgenden Befehlszeile ausgeführt: dotnet-trace Collect --clreventlevel 5 --clrevents Assemblyloader+loader+Exception+codesymbols --process-id
(dies ist erforderlich, da die Aufgabe außerhalb des Prozesses ausgeführt wird, da sie mit .NET10 erstellt wurde).
Dies führte zu den folgenden Ereignissen, kurz bevor die Ausnahme ausgelöst wurde:

Code: Select all

Provider Name/Event Name    Text
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Start    [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, NpmRegistry.Wrapper, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [RequestingAssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, FindInLoadContext], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, AssemblyLoadContextLoad], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/KnownPathProbed  [ClrInstanceID, 9], [FilePath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [Source, ApplicationAssemblies], [Result, 0]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, FindInLoadContext], [AssemblyLoadContext, Default], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, ApplicationAssemblies], [AssemblyLoadContext, Default], [Result, Success], [ResultAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ErrorMessage, ]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, DefaultAssemblyLoadContextFallback], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [Result, Success], [ResultAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ErrorMessage, ]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Stop [ClrInstanceID, 9], [AssemblyName, System.Net.Http.Json, Version=9.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, NpmRegistry.Wrapper, Version=1.3.0.0, Culture=neutral, PublicKeyToken=null], [AssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll" Microsoft.Build.Shared.MSBuildLoadContext #0], [RequestingAssemblyLoadContext, "MSBuild plugin E:\WebPages\personal.NET\_bin\_tools\Simulation\net10.0\Tasks.dll"  Microsoft.Build.Shared.MSBuildLoadContext #0], [Success, True], [ResultAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [Cached, False]
Microsoft-Windows-DotNETRuntime/Loader/AssemblyLoad [AssemblyID, 2354263771168], [AppDomainID, 2355714450368], [AssemblyFlags, ReadyToRun], [FullyQualifiedAssemblyName, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [BindingID, 0], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/Loader/ModuleLoad   [ModuleID, 140719892123472], [AssemblyID, 2354263771168], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ModuleNativePath, ], [ManagedPdbSignature, 7543d2be-596d-99e2-edcc-08858346c399], [ManagedPdbAge, 1], [ManagedPdbBuildPath, /_/src/runtime/artifacts/obj/System.Net.Http.Json/Release/net10.0/System.Net.Http.Json.pdb], [NativePdbSignature, 43cb33d4-065a-2db8-dfdf-2768c026b1dc], [NativePdbAge, 1], [NativePdbBuildPath, System.Net.Http.Json.ni.pdb], [ModuleILFileName, System.Net.Http.Json.dll]
Microsoft-Windows-DotNETRuntime/Loader/DomainModuleLoad [ModuleID, 140719892123472], [AssemblyID, 2354263771168], [AppDomainID, 2355714450368], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Net.Http.Json.dll], [ModuleNativePath, ], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Start    [ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyLoadContext, Default], [RequestingAssemblyLoadContext, Default]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/KnownPathProbed  [ClrInstanceID, 9], [FilePath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [Source, ApplicationAssemblies], [Result, 0]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, FindInLoadContext], [AssemblyLoadContext, Default], [Result, AssemblyNotFound], [ResultAssemblyName, ], [ResultAssemblyPath, ], [ErrorMessage, Could not locate assembly]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/ResolutionAttempted  [ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [Stage, ApplicationAssemblies], [AssemblyLoadContext, Default], [Result, Success], [ResultAssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [ErrorMessage, ]
Microsoft-Windows-DotNETRuntime/AssemblyLoader/Stop [ClrInstanceID, 9], [AssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyPath, ], [RequestingAssembly, System.Net.Http.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [AssemblyLoadContext, Default], [RequestingAssemblyLoadContext, Default], [Success, True], [ResultAssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [ResultAssemblyPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [Cached, False]
Microsoft-Windows-DotNETRuntime/Loader/AssemblyLoad [AssemblyID, 2354263765792], [AppDomainID, 2355714450368], [AssemblyFlags, ReadyToRun], [FullyQualifiedAssemblyName, System.Text.Json, Version=10.0.0.0, Culture=neutral, PublicKeyToken=cc7b13ffcd2ddd51], [BindingID, 0], [ClrInstanceID, 9]
Microsoft-Windows-DotNETRuntime/Loader/ModuleLoad   [ModuleID, 140719892128080], [AssemblyID, 2354263765792], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [ModuleNativePath, ], [ManagedPdbSignature, 598db87f-20be-99e5-3876-4757d3bbc727], [ManagedPdbAge, 1], [ManagedPdbBuildPath, /_/src/runtime/artifacts/obj/System.Text.Json/Release/net10.0/System.Text.Json.pdb], [NativePdbSignature, 36eb7d03-7580-e63a-3c25-1d4e2af43414], [NativePdbAge, 1], [NativePdbBuildPath, System.Text.Json.ni.pdb], [ModuleILFileName, System.Text.Json.dll]
Microsoft-Windows-DotNETRuntime/Loader/DomainModuleLoad [ModuleID, 140719892128080], [AssemblyID, 2354263765792], [AppDomainID, 2355714450368], [ModuleFlags, Manifest, ReadyToRunModule], [ModuleILPath, C:\Program Files\dotnet\shared\Microsoft.NETCore.App\10.0.1\System.Text.Json.dll], [ModuleNativePath, ], [ClrInstanceID,  9]
Microsoft-Windows-DotNETRuntime/Exception/Start [ExceptionType, System.MissingMethodException], [ExceptionMessage, Method not found: 'System.Threading.Tasks.Task`1 System.Net.Http.Json.HttpClientJsonExtensions.GetFromJsonAsync(System.Net.Http.HttpClient, System.String, System.Text.Json.Serialization.Metadata.JsonTypeInfo`1, System.Threading.CancellationToken)'.], [ExceptionEIP, 0x00000000], [ExceptionHRESULT, -2146233069], [ExceptionFlags, CLSCompliant], [ClrInstanceID, 9]
Dies zeigt, dass zuerst System.Net.Http.Json Version 10.0.0.0 geladen wird (obwohl Version 9.0.0.0 angefordert wird) und dann System.Text.Json Version 10.0.0.0. Gemäß den Microsoft-Windows-DotNETRuntime/AssemblyLoader/Stop-Ereignissen werden beide Assemblys korrekt geladen. Und direkt nach dem Laden von System.Text.Json wird die MissingMethodException ausgelöst.
Das NuGet-Paket, das die „fehlende Methode“ aufruft, wird mit .NET9 kompiliert, aber in meiner lokalen Kopie habe ich es auch für .NET10 kompiliert und das hat dieses Problem nicht behoben, also muss es immer noch etwas geben, das ich übersehen habe. Vielleicht benötige ich zusätzliche Ereignisse, um sie mit dotnet-trace zu protokollieren?
UPDATE 2
Durch das Debuggen der Aufgabe und das Abbrechen des Debuggers, wenn die MissingMethodException ausgelöst wird, ist es mir gelungen, den Stacktrace abzurufen:

Code: Select all

System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncMethodBuilderCore.Start(ref NpmRegistry.Wrapper.NpmRegistryClient.d__3 stateMachine) Line 38    C#
System.Private.CoreLib.dll!System.Runtime.CompilerServices.AsyncTaskMethodBuilder.Start(ref NpmRegistry.Wrapper.NpmRegistryClient.d__3 stateMachine) Line 35    C#
NpmRegistry.Wrapper.dll!NpmRegistry.Wrapper.NpmRegistryClient.GetPackageData(string name, string ns, System.Threading.CancellationToken cancellationToken) Line 37  Unknown
... 
Die Variable stateMachine wird vom Compiler generiert, da die aufgerufene Methode eine asynchrone Methode ist. Ich habe auch überprüft, ob es eine innere Ausnahme in der ausgelösten Ausnahme gibt, aber diese Eigenschaft ist null.
UPDATE 3
Die JsonTypeInfo wird mit dem folgenden Code generiert:

Code: Select all

using System.Text.Json;
using System.Text.Json.Serialization;

namespace NpmRegistry.Wrapper.Models;

[JsonSerializable(typeof(NpmPackage))]
[JsonSourceGenerationOptions(AllowTrailingCommas = true,
GenerationMode = JsonSourceGenerationMode.Default,
IgnoreReadOnlyFields = false,
IgnoreReadOnlyProperties = false,
IncludeFields = false,
MaxDepth = 15,
NumberHandling = JsonNumberHandling.Strict,
PreferredObjectCreationHandling = JsonObjectCreationHandling.Replace,
ReadCommentHandling = JsonCommentHandling.Skip,
UnknownTypeHandling = JsonUnknownTypeHandling.JsonElement,
UnmappedMemberHandling = JsonUnmappedMemberHandling.Skip,
UseStringEnumConverter = true,
WriteIndented = false)]
internal sealed partial class ModelsSerializerContext : JsonSerializerContext
{
}
und dann wie folgt an den Aufruf von GetFromJsonAsync übergeben: httpClient.GetFromJsonAsync(url, ModelsSerializerContext.Default.NpmPackage, cancellingToken);
UPDATE 4
Ich konnte die MissingMethodException loswerden, indem ich Code in der Task-Assembly entfernte, der auch a verwendet JsonSerializationContext zum Serialisieren und Deserialisieren einiger Objekte, die ich speziell in dieser Assembly verwende. Ich glaube nicht, dass es sich um einen Versionskonflikt für die System.Net.Http.Json-Assembly handelt, da dieselbe Ausnahme ausgelöst wird, wenn ich das NuGet-Paket selbst für .NET 10.0 erstelle.
Jetzt frage ich mich, warum das Entfernen meiner JSON-Serialisierung/Deserialisierung in der Tasks-Assembly dazu führt, dass die Deserialisierung im NuGet-Paket plötzlich erfolgreich ist.
UPDATE 5
Ein bisschen mehr Details zu dem Szenario, in dem es mit der MissingMethodException bricht (immer noch nur, wenn es als MSBuild-Aufgabe ausgeführt wird und NICHT, wenn es als eigenständige Anwendung ausgeführt wird).
Zur Übersicht auch eine kurze Zusammenfassung des Setups (einschließlich zusätzlicher Details, die für dieses Problem erforderlich sind). Ich habe eine benutzerdefinierte MSBuild-Aufgabe in C# implementiert und erstelle sie für .NET10. In meiner Aufgabe führe ich auch einige JSON-Analysen durch, wobei ich einen generierten JsonSerializerContext verwende. Dadurch ist die Task-Assembly auch AOT-kompatibel. Die benutzerdefinierte Aufgabe verwendet ein NuGet-Paket eines Drittanbieters (

Code: Select all

Fiedler.NpmRegistry.Wrapper
), der auch einen generierten JsonSerializerContext (seit Version 1.2.1) verwendet, um die Ergebnisse zu analysieren, die er von Registry.npmjs.org erhält, indem er die generierte JsonTypeInfo an einen Aufruf der HttpClient-Erweiterungsmethode GetFromJsonAsync (aus der System.Net.Http.Json-Assembly) übergibt. Derzeit ist dieses NuGet-Paket für .NET9 erstellt, aber das gleiche Problem besteht immer noch, wenn ich dieses Paket lokal für .NET10 erstelle. In beiden Fällen wird die System.Net.Http.Json-Assembly von .NET10 trotzdem geladen.
Sobald ein Code in meiner Task-Assembly JsonSerializer.Deserialize aufruft (egal ob vor oder nach dem Aufruf von GetFromJsonAsync), befindet sich die MissingMethodException im ersten Aufruf von GetFromJsonAsync. Durch die Verwendung eines generierten JsonSerializationContext irgendwo in meiner Aufgabenassembly wird die Ausnahme ausgelöst. Wenn ich nur den gesamten JSON-Parsing-Code generiere und ihn nirgendwo verwende, wird die Ausnahme NICHT ausgelöst.
Bitte beachten Sie auch, dass Version 1.2.0 von Fiedler.NpmRegistry.Wrapper keinen vorkompilierten JsonSerializerContext verwendet hat, in meinem Setup jedoch auch die Eigenschaften der Klassen aus dem JSON mithilfe von Reflektion nicht falsch zugewiesen werden konnten. Wahrscheinlich stimmt also irgendwo mit der JSON-Analyse etwas grundsätzlich nicht, wenn diese kompilierten JsonSerializerContexts verwendet werden.
Meiner Meinung nach wird es immer seltsamer.

Quick Reply

Change Text Case: 
   
  • Similar Topics
    Replies
    Views
    Last post