Rendimiento de rendimiento de XmlSerializer al especificar XmlRootAttribute

Actualmente estoy teniendo un problema realmente extraño y parece que no puedo resolver cómo resolverlo.

Tengo un tipo bastante complejo que bash serializar usando la clase XmlSerializer. Esto realmente funciona bien y el tipo se serializa correctamente, pero parece tomar mucho tiempo para hacerlo; alrededor de 5 segundos dependiendo de los datos en el objeto.

Después de un poco de perfilar, reduje el problema, curiosamente, a especificar un XmlRootAttribute cuando llamo a XmlSerializer.Serialize. Hago esto para cambiar el nombre de una colección que se está serializando desde ArrayOf a algo un poco más significativo. Una vez que elimine el parámetro, ¡la operación es casi instantánea!

¡Cualquier idea o sugerencia sería excelente ya que estoy totalmente perplejo con esta!

Solo para cualquier otra persona que se encuentre con este problema; armado con la respuesta anterior y el ejemplo de MSDN logré resolver este problema utilizando la siguiente clase:

public static class XmlSerializerCache { private static readonly Dictionary cache = new Dictionary(); public static XmlSerializer Create(Type type, XmlRootAttribute root) { var key = String.Format( CultureInfo.InvariantCulture, "{0}:{1}", type, root.ElementName); if (!cache.ContainsKey(key)) { cache.Add(key, new XmlSerializer(type, root)); } return cache[key]; } } 

Entonces, en lugar de usar el constructor XmlSerializer predeterminado que toma un XmlRootAttribute, utilizo lo siguiente en su lugar:

 var xmlRootAttribute = new XmlRootAttribute("ExampleElement"); var serializer = XmlSerializerCache.Create(target.GetType(), xmlRootAttribute); 

¡Mi aplicación ahora se está ejecutando nuevamente!

Como se menciona en el comentario de seguimiento de la pregunta original, .NET emite ensamblajes al crear XmlSerializers y almacena en caché el ensamblado generado si se crea usando uno de estos dos constructores:

 XmlSerializer(Type) XmlSerializer(Type, String) 

Los ensamblados generados utilizando los otros constructores no se almacenan en caché, por lo que .NET debe generar ensamblados nuevos cada vez.

¿Por qué? Probablemente, esta respuesta no sea muy satisfactoria, pero al observar esto en Reflector, puede ver que la clave utilizada para almacenar y acceder a los ensamblados XmlSerializer generados ( TempAssemblyCacheKey ) es simplemente una clave compuesta construida a partir del tipo serializable y (opcionalmente) su espacio de nombres

Por lo tanto, no hay un mecanismo para saber si un XmlSerializer caché para XmlSerializer tiene un XmlRootAttribute especial o el predeterminado.

Es difícil pensar en una razón técnica por la cual la clave no podría acomodar más elementos, por lo que esta es probablemente solo una característica que nadie tuvo tiempo de implementar (especialmente porque implicaría cambiar las clases por lo demás estables).

Es posible que haya visto esto, pero en caso de que no lo haya hecho, la documentación de la clase XmlSerializer analiza una solución alternativa:

Si utiliza cualquiera de los otros constructores, se generan múltiples versiones del mismo ensamblaje y nunca se descargan, lo que da como resultado una pérdida de memoria y un rendimiento deficiente. La solución más fácil es usar uno de los dos constructores mencionados anteriormente. De lo contrario, debe almacenar en caché los ensamblajes en una Hashtable, como se muestra en el siguiente ejemplo.

(He omitido el ejemplo aquí)

Solo tuve que implementar algo como esto y usé una versión ligeramente más optimizada de la solución de @ Dougc con una sobrecarga de conveniencia:

 public static class XmlSerializerCache { private static readonly Dictionary cache = new Dictionary(); public static XmlSerializer Get(Type type, XmlRootAttribute root) { var key = String.Format("{0}:{1}", type, root.ElementName); XmlSerializer ser; if (!cache.TryGetValue(key, out ser)) { ser = new XmlSerializer(type, root); cache.Add(key, ser); } return ser; } public static XmlSerializer Get(Type type, string root) { return Get(type, new XmlRootAttribute(root)); } } 

Aquí se explica una implementación más compleja. Sin embargo, el proyecto ya no está activo.

Las clases relevantes son visibles aquí: http://mvpxml.codeplex.com/SourceControl/changeset/view/64156#258382

En particular, la siguiente función para generar una clave única puede ser útil:

 public static string MakeKey(Type type , XmlAttributeOverrides overrides , Type[] types , XmlRootAttribute root , String defaultNamespace) { StringBuilder keyBuilder = new StringBuilder(); keyBuilder.Append(type.FullName); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetOverridesSignature(overrides)); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetTypeArraySignature(types)); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetXmlRootSignature(root)); keyBuilder.Append("??"); keyBuilder.Append(SignatureExtractor.GetDefaultNamespaceSignature(defaultNamespace)); return keyBuilder.ToString(); }