Serialisierung für einen OnAssemblyResolve-Handler kann zur Rekursion führen

In diesem Artikel können Sie das Problem beheben, bei dem die Serialisierung dazu führt, dass der Handler für die Assemblierungs Auflösung rekursiv aufgerufen wird.

Original Version des Produkts:   Microsoft .NET Framework 3.5 Service Pack 1, 4,5
Ursprüngliche KB-Nummer:   2756498

Problembeschreibung

Es wird davon ausgegangen, dass Sie eine Klasse mit dem XmlSerializerAssembly Attribut wie im folgenden Beispiel markiert haben:

[Serializable, XmlRoot(ElementName="Bindings", IsNullable=false), XmlSerializerAssembly(AssemblyName="Contoso.ObjectLoaders.Bindings")]
public class Bindings
{
    (...)
}

Die generierte Assembly wird im globalen Assemblycache (Global Assembly Cache, GAC) anstatt im Ordner bin signiert und gespeichert. Sie implementieren auch einen Handler für ein AppDomain.AssemblyResolve Ereignis, und Sie serialisieren eine Instanz der Klasse in der Assembly Resolver-Handler-Methode 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 Handler für die Assemblierungs Auflösung 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 geladen Assembly.LoadWithPartialName(string) , wodurch der OnResolveAssembly Handler erneut aufgerufen wird.

Problemumgehung 1: Entfernen des XmlSerializerAssembly-Attributs

Diese Methode verhindert, dass die Assembly geladen wird Assembly.LoadWithPartialName(string) .

Problemumgehung 2: Schreiben eines robusteren Assembly-Auflösungs Handlers

Es handelt sich um die empfohlene Problemumgehung und löst alle Probleme im Zusammenhang mit der Rekursion. Das folgende Beispiel ist eine einfache Codevorlage für diese Problemumgehung. Der Hauptgedanke besteht darin, die bereits aufgelösten Assemblys in eine generische Liste aufzufügen (der gleichzeitige Sack wurde aufgrund seines Thread Safes ausgewählt) und zurückzugeben, wenn sich die Assembly bereits im Prozess der Lösung befand.

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);
    }
}

Informationsquellen