Периодические ошибки при десериализации объекта из XML - PullRequest
8 голосов
/ 25 января 2011

У меня есть программа, которая берет объекты, хранящиеся в виде 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<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];
    }
}

Ответы [ 4 ]

9 голосов
/ 06 июня 2011

Вы можете предварительно создать сериализаторы: http://msdn.microsoft.com/en-us/library/bk3w6240%28v=VS.100%29.aspx Просто попробуйте. Следующий канонический кандидат для таких проблем - ваш антивирусный сканер. Ваш инструмент записывает на диск при создании сериализаторов. Я видел, как в таких ситуациях вирусный сканер выдавал странные ошибки.

1 голос
/ 06 июня 2011

XmlSerializer - предполагается для обеспечения безопасности потока.

Даже если это так, вы можете заметить, что поведение, которое вы получаете, в обоих случаях терпит неудачу: XmlSerializer..ctor(Type 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. Таким образом, вы создаете только 1 сериализатор за раз. Удар мал, если вы не обрабатываете тонны разных типов.

Обратите внимание, что в любом случае вам нужна блокировка, так как вы можете получить дубликаты исключений, когда 2 типа пытаются быть добавлены одновременно.

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

Если вышеприведенного все еще недостаточно, я бы попробовал использовать блокировку чтения / записи в конструкторе сериализатора и использование сериализаторов. Суть в том, что, возможно, проблема многопоточности стоит больше, чем 2 ctor, работающих одновременно.

Все вышесказанное - Огромное предположение, но если бы это был я, я бы точно подтвердил, что это не так.

0 голосов
/ 18 июля 2013

При первой ошибке (не удается выполнить программу) вы можете столкнуться с той же ошибкой XmlSerializer , с которой мы столкнулись. Оказывается, XmlSerlializer выдает это исключение, когда для Directory.CurrentDirectory задана папка, которая больше не существует.

Наша конкретная ситуация отличается от вашей, но я приведу подробности на случай, если она поможет пролить свет на то, что может случиться с вами, или поможет кому-то еще. В нашем случае небольшое количество наших клиентов получит эту ошибку после запуска нашего приложения 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 . Исправление заключалось в том, чтобы SetCurrentDirectory находился в допустимом месте во время инициализации приложения, прежде чем произойдет какая-либо сериализация.

0 голосов
/ 25 января 2011

Это признак того, что вы не кэшируете свои сериализаторы, что совсем не хорошо => это приводит к утечке памяти, и я подозреваю, что вы испытаете это.

Помните, что .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));
                }
            }
        }
    }

}
...