6

У меня возникла очень неприятная проблема, которую я не могу определить.
Я запускаю очень большое приложение ASP.Net для бизнеса, содержащее много тысяч объектов; Он использует сериализацию/десериализацию в памяти с MemoryStream для клонирования состояния приложения (договоров страхования) и передачи его другим модулям. Он работал отлично в течение многих лет. Теперь иногда систематически не, в сериализации он бросает исключениеКонструктор десятичного байтового массива в Binaryformatter Serialization

Десятичного конструктор байтового массива требует массива длины четыре, содержащих действительные десятичных байты.

Запуск того же приложения с теми же данными, 3 раза из 5 он работает. Я включил все исключения CLR, Debug - Исключения - исключение CLR - включено, , поэтому я предполагаю, что если произойдет неправильная инициализация/назначение в десятичное поле, программа должна остановиться. Этого не происходит.
Я попытался разделить сериализацию на более элементарные объекты, но это очень сложно, чтобы попытаться определить поле, вызывающее проблему. Из рабочей версии в производстве и этого я передал от .Net 3.5 до .NET 4.0 и были сделаны последовательные изменения в части пользовательского интерфейса, а не в бизнес-части. Терпеливо я пройду все изменения.

Это похоже на старомодные проблемы C, когда char *p пишет, где это не должно быть, и только в процессе сериализации, когда он анализирует все данные, проблема вылетает.

Возможно ли это в управляемой среде .Net? Приложение огромно, но я не вижу аномальных темпов памяти. Что может быть способом отладки и отслеживания проблемы?

Ниже часть StackTrace

[ArgumentException: Decimal byte array constructor requires an array of length four containing valid decimal bytes.] 
    System.Decimal.OnSerializing(StreamingContext ctx) +260 

[SerializationException: Value was either too large or too small for a Decimal.] 
    System.Decimal.OnSerializing(StreamingContext ctx) +6108865 
    System.Runtime.Serialization.SerializationEvents.InvokeOnSerializing(Object obj, StreamingContext context) +341 
    System.Runtime.Serialization.Formatters.Binary.WriteObjectInfo.InitSerialize(Object obj, ISurrogateSelector surrogateSelector, StreamingContext context, SerObjectInfoInit serObjectInfoInit, IFormatterConverter converter, ObjectWriter objectWriter, SerializationBinder binder) +448 
    System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Write(WriteObjectInfo objectInfo, NameInfo memberNameInfo, NameInfo typeNameInfo) +969 
    System.Runtime.Serialization.Formatters.Binary.ObjectWriter.Serialize(Object graph, Header[] inHeaders, __BinaryWriter serWriter, Boolean fCheck) +1016 
    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph, Header[] headers, Boolean fCheck) +319 
    System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Serialize(Stream serializationStream, Object graph) +17 
    Allianz.Framework.Helpers.BinaryUtilities.SerializeCompressObject(Object obj) in D:\SVN\SUV\branches\SUVKendo\DotNet\Framework\Allianz.Framework.Helpers\BinaryUtilities.cs:98 
    Allianz.Framework.Session.State.BusinessLayer.BLState.SaveNewState(State state) in 

Извините за длинную историю и неопределенном вопрос, я буду очень признателен за любую помощь.

+0

Я столкнулся с той же ошибкой, это оказалось вызвано объединением (используя StructLayout (LayoutKind.Explicit) w здесь я интерпретировал bool как десятичное случайно. Остальная часть приложения использовала десятичное значение как нормальное значение 0, но только во время десериализации в отдельном приложении он бы выбросил эту ошибку, я понял проблему, выполнив Decimal.GetBytes до сериализации и заметив, что третий-последний байт был 1 вместо 0 как и следовало ожидать. – BrandonAGr

ответ

3

То есть .... очень интересно; который на самом деле не читает или не записывает данные в это время - он вызывает обратный вызов перед сериализацией, aka [OnSerializing], который здесь отображается на decimal.OnSerializing. Что , что это попытка здравомыслия - проверьте бит - но похоже, что в BCL есть просто ошибка. Вот реализация в 4.5 (кашель «рефлектора» кашель):

[OnSerializing] 
private void OnSerializing(StreamingContext ctx) 
{ 
    try 
    { 
     this.SetBits(GetBits(this)); 
    } 
    catch (ArgumentException exception) 
    { 
     throw new SerializationException(Environment.GetResourceString("Overflow_Decimal"), exception); 
    } 
} 

GetBits получает/середина/Hi массив Lo/флагов, так что мы можем быть уверены, что массив передается SetBits не равен нулю и правильная длина. Так что не получится, та часть, которая должна быть не суметь в SetBits, здесь:

int num = bits[3]; 
    if (((num & 2130771967) == 0) && ((num & 16711680) <= 1835008)) 
    { 
     this.lo = bits[0]; 
     this.mid = bits[1]; 
     this.hi = bits[2]; 
     this.flags = num; 
     return; 
    } 

В принципе, если тест if проходит мы получаем в назначьте ценности и выход успешно; если ошибка ifне удалась, это приводит к исключению. bits[3] - это кусок flags, который содержит знак и масштаб, IIRC. Итак, вопрос здесь: как вы получили недопустимый decimal с разбитым куском flags?

Примечание здесь: 2130771967 это маска:

0111 1111 0000 0000 1111 1111 1111 1111 

16711680 является маска:

0000 0000 1111 1111 0000 0000 0000 0000 

и 1835008 это маска

0000 0000 0001 1100 0000 0000 0000 0000 

(который является десятичное 28 в верхнее слово)

для цитаты из MSDN:

Четвертый элемент возвращаемого массива содержит масштабный коэффициент и знак . Он состоит из следующих частей: биты с 0 по 15, нижнее слово , не используются и должны быть равны нулю. Биты с 16 по 23 должны содержать показатель между 0 и 28, который указывает мощность 10 для деления на целое число. Биты с 24 по 30 не используются и должны быть равны нулю. Бит 31 содержит знак: 0 означает положительный, а 1 означает отрицательный.

Так не проходит этот тест:

  • показатель является недействительным (за пределами 0-28)
  • нижнего слово ненулевого
  • верхних байты (исключая MSB) является ненулевая

К сожалению, у меня нет волшебного способа нахождения которых decimal недействителен ...

Единственными способами я могу думать, глядя здесь:

  • разброс GetBits/new decimal(bits) по всему код - возможно, как void SanityCheck(this decimal) метод (возможно с [Conditional("DEBUG")] или что-то)
  • добавить [OnSerializing] методы в ваш главный например, где-нибудь (консоль может быть), чтобы вы могли видеть, на каком объекте он работал, когда он взорвался.
+0

Сообщение об исключении кажется довольно ошибочным - речь идет о массиве из 4 "десятичных байтов", и нет такого конструктора для 'decimal' ... И что такое« десятичный байт »? –

+0

@MatthewWatson действительно - мы даже не используем конструктор здесь; обычно, 'SetBits' вызывается' public Decimal (int [] bits) ', который как бы объясняет это сообщение, но да: запутанный. –