从XML反序列化对象时出现间歇性错误

我有一个程序,它将存储为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) ..... 

或者我会得到这个:

 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) .... 

该程序每天成功处理数千条消息,但我每天只能得到这些错误2到3次。 它们似乎与任何特定类型的消息无关,只是完全随机。

知道导致这些错误的原因以及解决方法吗?

ETA – 以下是导致错误的代码,如果有帮助:

 public class MessageContextBuilder where T : MessageContextBase { private static IDictionary SerializerCache { get; set; } public ILog Logger { get; set; } public MessageContextBuilder() { if (SerializerCache == null) SerializerCache = new Dictionary(); Logger = LogContextManager.Context.GetLogger<MessageContextBuilder>(); } 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]; } } 

您可以预先创建序列化程序: http : //msdn.microsoft.com/en-us/library/bk3w6240%28v=VS.100%29.aspx试一试。 针对此类问题的下一个规范候选者是您的病毒扫描程序。 您的工具在创建序列化程序时正在写入光盘。 我见过病毒扫描程序在这种情况下会产生各种奇怪的错误。

XmlSerializer 应该是线程安全的。

即使是这种情况,你可以注意到你在两种情况下都失败的行为: XmlSerializer..ctor(Type type)

鉴于此,它看起来像尝试创建序列化程序的multithreading限制。

我建议您使用以下代码:

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

并在Add上实现锁定。 这样,您一次只能创建1个序列化程序。 如果你没有处理大量不同类型,那么命中率很小。

请注意,无论如何您都需要锁定,因为当您尝试同时添加两种类型时,您可以获得重复的exception。

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

如果上面仍然不够,我会尝试使用串行器构造函数与序列化程序使用的读/写锁。 可能是multithreading问题的思路比同时运行的2个ctors值得。

以上都是一个巨大的猜测,但如果是我,我肯定会证实不是这样。

这是一个迹象表明你没有缓存你的串行器,这根本不是很好=>它会导致内存泄漏,我怀疑你会遇到这种情况。

请记住, 每次创建序列化程序 ,.NET都会生成代码并将它们编译为程序集。

始终创建序列化程序,然后缓存它们。

这是一个示例:

 public class SerialiserCache { private static readonly SerialiserCache _current = new SerialiserCache(); private Dictionary _cache = new Dictionary(); 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)); } } } } } 

对于第一个错误(无法执行程序),您可能遇到了我们遇到的相同XmlSerializer错误 。 事实certificate,当Directory.CurrentDirectory设置为不再存在的文件夹时,XmlSerlializer会抛出该exception。

我们的具体情况与您的情况不同,但我会提供详细信息,以防有助于阐明可能发生的事情或帮助其他人。 在我们的例子中,我们的少数客户在直接从安装程序启动WinForms应用程序后会收到该错误,即他们在安装或升级后选择“立即运行”选项。 (不清楚为什么它发生在某些人而不是其他人身上)。 我们怀疑发生的事情是我们的安装程序(InstallAware)偶尔启动我们的应用程序,当前目录设置为不再存在或即将被删除的文件夹。 为了测试这个理论,我编写了一个模拟从安装程序启动的测试应用程序:

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

果然,只要启动的应用程序创建了XmlSerializer的新实例,就会抛出exception。 我放入trace语句来显示GetCurrentDirectory()的结果,实际上它被设置为WillBeDeleted文件夹。 在任何序列化发生之前,修复程序是在应用程序初始化期间将SetCurrentDirectory置于有效位置。