2011-01-25 16 views
8

Tengo un programa que toma los objetos almacenados como XML en una base de datos (básicamente una cola de mensajes) y los deserializa. Intermitentemente, voy a tener uno de los siguientes errores:Errores intermitentes al deserializar el objeto desde XML

System.Runtime.InteropServices.ExternalException: Cannot execute a program. The command being executed was "C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\csc.exe" /noconfig /fullpaths @"C:\Documents and Settings\useraccount\Local Settings\Temp\lh21vp3m.cmdline". 
    at System.CodeDom.Compiler.Executor.ExecWaitWithCaptureUnimpersonated(SafeUserTokenHandle userToken, String cmd, String currentDir, TempFileCollection tempFiles, String& outputName, String& errorName, String trueCmdLine) 
    at System.CodeDom.Compiler.Executor.ExecWaitWithCapture(SafeUserTokenHandle userToken, String cmd, String currentDir, TempFileCollection tempFiles, String& outputName, String& errorName, String trueCmdLine) 
    at Microsoft.CSharp.CSharpCodeGenerator.Compile(CompilerParameters options, String compilerDirectory, String compilerExe, String arguments, String& outputFile, Int32& nativeReturnValue, String trueArgs) 
    at Microsoft.CSharp.CSharpCodeGenerator.FromFileBatch(CompilerParameters options, String[] fileNames) 
    at Microsoft.CSharp.CSharpCodeGenerator.FromSourceBatch(CompilerParameters options, String[] sources) 
    at Microsoft.CSharp.CSharpCodeGenerator.System.CodeDom.Compiler.ICodeCompiler.CompileAssemblyFromSourceBatch(CompilerParameters options, String[] sources) 
    at System.CodeDom.Compiler.CodeDomProvider.CompileAssemblyFromSource(CompilerParameters options, String[] sources) 
    at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence) 
    at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies) 
    at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence) 
    at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace) 
    at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace) 
    at System.Xml.Serialization.XmlSerializer..ctor(Type type) 
    ..... 

O voy a conseguir éste:

System.InvalidOperationException: Unable to generate a temporary class (result=1). 
error CS0016: Could not write to output file 'c:\Documents and Settings\useraccount\Local Settings\Temp\nciktsd7.dll' -- 'Could not execute CVTRES.EXE.' 

    at System.Xml.Serialization.Compiler.Compile(Assembly parent, String ns, XmlSerializerCompilerParameters xmlParameters, Evidence evidence) 
    at System.Xml.Serialization.TempAssembly.GenerateAssembly(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, Evidence evidence, XmlSerializerCompilerParameters parameters, Assembly assembly, Hashtable assemblies) 
    at System.Xml.Serialization.TempAssembly..ctor(XmlMapping[] xmlMappings, Type[] types, String defaultNamespace, String location, Evidence evidence) 
    at System.Xml.Serialization.XmlSerializer.GenerateTempAssembly(XmlMapping xmlMapping, Type type, String defaultNamespace) 
    at System.Xml.Serialization.XmlSerializer..ctor(Type type, String defaultNamespace) 
    at System.Xml.Serialization.XmlSerializer..ctor(Type type) 
    .... 

la secuencia de programa miles de mensajes al día con éxito, pero solo me dan estos errores tal vez 2 o 3 veces al día. No parecen estar correlacionados con ningún tipo específico de mensaje, solo completamente al azar.

¿Alguna idea de qué causa esos errores y cómo solucionarlos?

ETA - Este es el código que está causando los errores, en caso de que ayuda a:

public class MessageContextBuilder<T> where T : MessageContextBase 
{ 
    private static IDictionary<string, XmlSerializer> SerializerCache { get; set; } 
    public ILog Logger { get; set; } 


    public MessageContextBuilder() { 
     if (SerializerCache == null) SerializerCache = new Dictionary<string, XmlSerializer>(); 
     Logger = LogContextManager.Context.GetLogger<MessageContextBuilder<T>>(); 
    } 

    public T BuildContextFromMessage(IEmailQueueMessage msg) { 
     XmlSerializer serializer = GetSerializer(typeof(T)); 
     XmlReader r = XmlReader.Create(new StringReader(msg.MessageDetails)); 
     if (serializer.CanDeserialize(r)) { 
      T rval = (T)serializer.Deserialize(r); 
      rval.EmailAddress = msg.EmailAddress; 
      rval.LocaleID = msg.LocaleID; 
      rval.StoreID = msg.StoreID; 
      rval.MessageID = msg.UniqueKey; 
      return rval; 
     } else { 
      throw new ArgumentException("Cannot deserialize XML in message details for message #" + msg.UniqueKey); 
     } 
    } 

    public XmlSerializer GetSerializer(Type t) { 
     if (!SerializerCache.ContainsKey(t.FullName)) { 
      SerializerCache.Add(t.FullName, new XmlSerializer(t)); // Error occurs here, in XmlSerializer constructor, intermittently 
     } 
     return SerializerCache[t.FullName]; 
    } 
} 

Respuesta

9

Puede pre-crear serializadores: http://msdn.microsoft.com/en-us/library/bk3w6240%28v=VS.100%29.aspx Sólo inténtelo. El próximo candidato canónico para tales problemas es su escáner de virus. Su herramienta está escribiendo en un disco mientras crea serializadores. He visto un antivirus que produce todo tipo de errores extraños en tales situaciones.

+0

+1, Algunos sitios han notado [el error CVTRES.EXE con el antivirus como las herramientas] (http://imak47.wordpress.com/2009/06/07/could-not-execute-cvtres-exe/) ejecutándose. – user7116

+1

+1 también - Creo que estás en algo con la idea del escáner de virus. En cuanto a la pre-creación de los serializadores, también es una buena opción, aunque podría ser un poco complicado para la aplicación en particular con la que estoy trabajando. –

+0

+1 que realmente suena como – eglasius

0

Esta es una señal de que no está de almacenamiento en caché tus serializadores que no es bueno en absoluto => que conduce a la memoria gotear y sospecho que experimentará esto.

Recuerde que .NET genera código y los compila en los conjuntos cada vez que cree un serializador.

Siempre cree sus serializadores y luego en la memoria caché.

Este es un ejemplo:

public class SerialiserCache 
{ 

    private static readonly SerialiserCache _current = new SerialiserCache(); 
    private Dictionary<Type, XmlSerializer> _cache = new Dictionary<Type, XmlSerializer>(); 

    private SerialiserCache() 
    { 

    } 

    public static SerialiserCache Current 
    { 
     get { return _current; } 
    } 

    public XmlSerializer this[Type t] 
    { 
     get 
     { 
      LoadIfNecessary(t); 
      return _cache[t]; 
     } 
    } 

    private void LoadIfNecessary(Type t) 
    { 

     // double if to prevent race conditions 
     if (!_cache.ContainsKey(t)) 
     { 
      lock (_cache) 
      { 
       if (!_cache.ContainsKey(t)) 
       { 
        _cache[t] = new XmlSerializer(typeof(T)); 
       } 
      } 
     } 
    } 

} 
+0

Un buen consejo, y lo veré, pero de la forma en que está configurado este programa, probablemente no importe. Básicamente se ejecuta como un trabajo cada pocos minutos, procesa algunos mensajes y luego sale. Entonces el almacenamiento en caché de los serializadores sería de beneficio mínimo. –

+0

Probablemente lo haga si las está creando muchas veces durante el mismo ruun. – Aliostad

+0

Según esta pregunta, .NET Framework almacena en caché los serializadores internamente (no estoy usando XmlAttributeOverrides). ¿No es ese el caso? http://stackoverflow.com/questions/678676/precompile-xmlserializers-with-xmlattributeoverrides –

1

XmlSerializer es supuesta ser seguro para subprocesos.

Incluso si ese es el caso, se puede notar el comportamiento que está recibiendo es en ambos casos fallan en: XmlSerializer..ctor(Type type)

Teniendo en cuenta que, en serio vea como una limitación multi-threading tratando de crear serializadores.

que sugieren que tomar este código tiene:

public XmlSerializer GetSerializer(Type t) { 
     if (!SerializerCache.ContainsKey(t.FullName)) { 
      SerializerCache.Add(t.FullName, new XmlSerializer(t)); // Error occurs here, intermittently 
     } 
     return SerializerCache[t.FullName]; 
    } 

y poner en práctica un bloqueo en la opción Agregar. De esta forma, solo está creando 1 serializador a la vez. El golpe es pequeño si no está procesando toneladas de diferentes tipos.

Tenga en cuenta que necesita el bloqueo de todos modos, ya que es posible obtener duplicados de excepciones cuando se intenta agregar 2 tipos al mismo tiempo.

static object serializerCacheLock = new object(); 
public XmlSerializer GetSerializer(Type t) { 
     if (!SerializerCache.ContainsKey(t.FullName)) 
     lock(serializerCacheLock) 
     if (!SerializerCache.ContainsKey(t.FullName)) { 
      SerializerCache.Add(t.FullName, new XmlSerializer(t)); 
     } 
     return SerializerCache[t.FullName]; 
    } 

Si lo anterior todavía no es suficiente, me gustaría probar con un bloqueo de lectura/escritura en el constructor de serializador frente a uso serializadores. La línea de pensamiento es que tal vez el problema de multi-threading vale más que 2 ctors corriendo al mismo tiempo.

Todo lo anterior es una suposición enorme, pero si fuera yo, definitivamente confirmaría que no es eso.

+0

Un buen punto, pero la aplicación no es de subprocesos múltiples, por lo que esto no debería ser un problema en mi caso. –

+0

@Eric, ¿no es un servicio web o algo así? ¿Se está procesando un programa del lado del cliente a través de un registro de mensajes, que se realiza de a uno por vez? – eglasius

+0

eso es correcto. Es una aplicación de línea de comandos que se ejecuta como una tarea programada y procesa registros de una "cola" en una tabla de base de datos de uno en uno. –

0

Para el primer error (no se puede ejecutar un programa), es posible que se encuentre con el mismo XmlSerializer bug con el que nos encontramos. Resulta que XmlSerializador arroja esa excepción cuando Directory.CurrentDirectory está configurado en una carpeta que ya no existe.

Nuestra situación específica es diferente a la suya, pero le daré los detalles en caso de que ayude a arrojar luz sobre lo que podría estar sucediendo para usted, o si ayuda a alguien más. En nuestro caso, un pequeño número de nuestros clientes recibiría ese error después de iniciar nuestra aplicación WinForms directamente desde el instalador, es decir, eligieron la opción "ejecutar ahora" después de instalar o actualizar. (Incierto por qué le sucedió a algunos pero no a otros). Lo que sospechamos que está sucediendo es que nuestro instalador (InstallAware) de vez en cuando inicia nuestra aplicación con el directorio actual configurado en una carpeta que ya no existe o que está a punto de eliminarse. Para probar esta teoría, escribí una aplicación de prueba que simula el lanzamiento del instalador:

string dir = @"C:\Users\me\Documents\Temp\WillBeDeleted"; 
    Directory.CreateDirectory(dir); 
    Directory.SetCurrentDirectory(dir); 

    Process.Start(@"C:\Program Files (x86)\...\our.exe"); 

    Directory.SetCurrentDirectory(@"C:\"); // otherwise, won't be able to delete 
    Directory.Delete(dir); 

Efectivamente, tan pronto como la aplicación iniciada crea una nueva instancia de la clase XmlSerializer, la excepción sería arrojado. Puse en las declaraciones de seguimiento para mostrar el resultado de GetCurrentDirectory(), y de hecho se estableció en la WillBeDeleted carpeta. La solución fue establecer SetCurrentDirectory en una ubicación válida durante la inicialización de la aplicación, antes de que tuviera lugar la serialización.

Cuestiones relacionadas