Die Serialisierung für einen OnAssemblyResolve-Handler kann zu Rekursionen führen.
Dieser Artikel hilft Ihnen, das Problem zu beheben, bei dem die Serialisierung dazu führt, dass der Assembly resolver-Handler rekursiv aufgerufen wird.
Ursprüngliche Produktversion: .NET Framework 3.5 Service Pack 1, 4.5
Ursprüngliche KB-Nummer: 2756498
Problembeschreibung
Angenommen, Sie haben eine Klasse mit dem XmlSerializerAssembly Attribut markiert, wie im folgenden Beispiel gezeigt:
[Serializable, XmlRoot(ElementName="Bindings", IsNullable=false), XmlSerializerAssembly(AssemblyName="Contoso.ObjectLoaders.Bindings")]
public class Bindings
{
(...)
}
Die generierte Assembly wird signiert und im globalen Assemblycache (Global Assembly Cache, GAC) statt im Bin-Ordner abgelegt. Sie implementieren auch einen Handler für AppDomain.AssemblyResolve das Ereignis und serialisieren eine Instanz der Klasse in der Assembly resolver-Handlermethode wie folgt:
private static Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
// Some code here
var serializer = new XmlSerializer(typeof(Bindings));
// Some more code here
}
In diesem Szenario führt die Serialisierung dazu, dass der Assembly resolver-Handler rekursiv aufgerufen wird, was zu einem Stapelüberlauf führen kann.
Ursache
Es handelt sich hierbei um ein beabsichtigtes Verhalten. Assemblys mit dem XmlSerializerAssembly Attribut werden mit Assembly.LoadWithPartialName(string) geladen, wodurch der Handler erneut aufgerufen OnResolveAssembly wird.
Problemumgehung 1: XmlSerializerAssembly-Attribut entfernen
Diese Methode verhindert, dass die Assembly von geladen Assembly.LoadWithPartialName(string) wird.
Problemumgehung 2: Schreiben eines robusteren Assembly Resolve-Handlers
Dies ist die empfohlene Problemumgehung und löst alle Probleme im Zusammenhang mit Rekursionen. Das folgende Beispiel ist eine einfache Codevorlage für diese Problemumgehung. Die Hauptidee besteht darin, die bereits aufgelösten Assemblys einer generischen Liste hinzuzufügen (der gleichzeitige Behälter wurde ausgewählt, da der Thread sicher ist) und zurückzugeben, wenn die Assembly bereits aufgelöst wird.
static ConcurrentBag<string> listOfAssemblies = new ConcurrentBag<string>();
private static Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
if (listOfAssemblies.Contains(args.Name))
{
// Already resolving this assembly, return now
return null;
}
try
{
listOfAssemblies.Add(args.Name);
// Add your handler code here
}
finally
{
// Assembly was handled, remove from list
listOfAssemblies.Remove(args.Name);
}
}