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