C# 从XML反序列化对象时出现间歇性错误
我有一个程序,它将对象作为XML存储在数据库(基本上是一个消息队列)中并反序列化它们。我会间歇性地出现以下错误之一:C# 从XML反序列化对象时出现间歇性错误,c#,.net,.net-3.5,xml-serialization,C#,.net,.net 3.5,Xml Serialization,我有一个程序,它将对象作为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:\Do
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<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];
}
}
公共类MessageContextBuilder,其中T:MessageContextBase
{
私有静态IDictionary SerializerCache{get;set;}
公共ILog记录器{get;set;}
public MessageContextBuilder(){
如果(SerializerCache==null)SerializerCache=new Dictionary();
Logger=LogContextManager.Context.GetLogger();
}
公共T BuildContextFromMessage(IEmailQueueMessage消息){
XmlSerializer serializer=GetSerializer(typeof(T));
XmlReader r=XmlReader.Create(新的StringReader(msg.MessageDetails));
if(序列化程序CanDeserialize(r)){
T rval=(T)序列化程序。反序列化(r);
rval.EmailAddress=msg.EmailAddress;
rval.LocaleID=msg.LocaleID;
rval.StoreID=msg.StoreID;
rval.MessageID=msg.UniqueKey;
返回rval;
}否则{
抛出新ArgumentException(“无法反序列化消息#的消息详细信息中的XML”+msg.UniqueKey);
}
}
公共XmlSerializer GetSerializer(类型t){
如果(!SerializerCache.ContainsKey(t.FullName)){
SerializerCache.Add(t.FullName,新的XmlSerializer(t));//此处在XmlSerializer构造函数中间歇性发生错误
}
返回SerializerCache[t.FullName];
}
}
这是一个迹象,表明您没有缓存一点都不好的序列化程序=>这会导致内存泄漏,我怀疑您会遇到这种情况
请记住,每次创建序列化程序时,.NET都会生成代码并将其编译成程序集
始终创建序列化服务器,然后缓存它们
以下是一个示例:
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));
}
}
}
}
}
公共类序列化缓存
{
私有静态只读SerialiserCache _current=new SerialiserCache();
私有字典_cache=新字典();
私有SerialiserCache()
{
}
当前公共静态序列化缓存
{
获取{return\u current;}
}
公共XmlSerializer此[类型t]
{
得到
{
如有需要(t);
返回_缓存[t];
}
}
专用空心荷载(如有必要)(t型)
{
//加倍if以防止比赛条件
if(!\u cache.ContainsKey(t))
{
锁(_缓存)
{
if(!\u cache.ContainsKey(t))
{
_cache[t]=新的XmlSerializer(typeof(t));
}
}
}
}
}
XmlSerializer被认为是线程安全的
即使是这种情况,您也可以注意到,在这两种情况下,您得到的行为都在以下位置失败:XmlSerializer..ctor(Type)
考虑到这一点,试图创建序列化程序看起来很像是多线程限制
我建议您使用以下代码:
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上实现一个锁。这样,一次只能创建一个序列化程序。如果你没有处理成吨的不同类型,那么影响很小
请注意,无论如何都需要锁,因为如果同时添加两个类型,可能会出现重复的异常
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];
}
如果以上仍然不够,我会尝试对序列化器构造函数使用读/写锁,而不是序列化器使用。我们的思路是,多线程问题可能比同时运行两个CTOR更有价值
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];
}
所有这些都是一个巨大的猜测,但如果是我,我肯定不会这样做。你可以预先创建序列化程序:试试看。这类问题的下一个典型候选对象是您的病毒扫描程序。创建序列化程序时,工具正在写入光盘。我见过病毒扫描程序在这种情况下产生各种奇怪的错误。对于第一个错误(无法执行程序),您可能遇到了与我们遇到的相同的错误。当Directory.CurrentDirectory设置为不再存在的文件夹时,XmlSerlializer会引发该异常 我们的具体情况与你们的不同,但我会给出细节,以防它有助于了解可能发生的事情,或者它有助于其他人。在我们的案例中,少数客户在直接从安装程序启动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的新实例,就会抛出异常。我放入跟踪语句以显示GetCurrentDirectory()的结果,实际上它被设置为WillBeDeleted文件夹。修复方法是在应用程序初始化期间,在进行任何序列化之前,将CurrentDirectory设置到一个有效位置。这是一个很好的技巧,我将对此进行介绍,但这与该程序的设置方式无关。基本上,它每隔几分钟作为作业运行一次,处理几条消息,然后退出。因此,缓存序列化程序的好处微乎其微。如果在同一个ruun中多次创建序列化程序,则可能会有好处