La serialización en un controlador OnAssemblyResolve puede causar recursividad
Este artículo le ayudará a resolver el problema en el que la serialización hará que se llame recursivamente al controlador de resolución de ensamblados.
Versión del producto original: .NET Framework 3,5 Service Pack 1, 4,5
Número de KB original: 2756498
Síntomas
Supongamos que tiene una clase marcada con el XmlSerializerAssembly
atributo como en el ejemplo siguiente:
[Serializable, XmlRoot(ElementName="Bindings", IsNullable=false), XmlSerializerAssembly(AssemblyName="Contoso.ObjectLoaders.Bindings")]
public class Bindings
{
(...)
}
El ensamblado generado se firma y se coloca en la memoria caché de ensamblados global (GAC) en lugar de en la carpeta bin . También puede implementar un controlador para el AppDomain.AssemblyResolve
evento y serializar una instancia de la clase en el método de controlador de resolución de ensamblados de la siguiente manera:
private static Assembly ResolveAssemblies(object sender, ResolveEventArgs args)
{
// Some code here
var serializer = new XmlSerializer(typeof(Bindings));
// Some more code here
}
En este escenario, la serialización hará que se llame de forma recursiva al controlador de resolución de ensamblados, lo que puede provocar un desbordamiento de pila.
Causa
Este comportamiento es una característica del diseño de la aplicación. Los ensamblados con el XmlSerializerAssembly
atributo se cargan con Assembly.LoadWithPartialName(string)
, que llamará de OnResolveAssembly
nuevo al controlador.
Solución 1: quitar el atributo XmlSerializerAssembly
Este método evita que el ensamblado se cargue con Assembly.LoadWithPartialName(string)
.
Solución 2: escribir un controlador de resolución de ensamblado más sólido
Se trata de la solución recomendada y resolverá los problemas relacionados con la recursividad. El siguiente ejemplo es una plantilla de código simple para esta solución alternativa. La idea principal es agregar los ensamblados que ya se han resuelto a una lista genérica (se eligió el contenedor simultáneo, porque es seguro para subprocesos) y devolverse si el ensamblado ya está en proceso de resolverse.
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);
}
}