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

Referencias