Десериализация с очень низкой производительностью с использованием datacontractserializer в приложении Silverlight - PullRequest
3 голосов
/ 05 марта 2010

Вот ситуация:

Приложение Silverlight 3 обращается к службе WCF, размещенной на asp.net, чтобы получить список элементов для отображения в сетке. Как только список передается клиенту, он кэшируется в IsolatedStorage. Это делается с помощью DataContractSerializer для сериализации всех этих объектов в поток, который затем архивируется и затем шифруется. Когда приложение перезапускается, оно сначала загружается из кэша (в обратном порядке) и десериализует объекты с помощью метода DataContractSerializer.ReadObject (). До недавнего времени все это прекрасно работало при всех сценариях, когда весь путь «загрузки из кэша» (расшифровывать / разархивировать / десериализовать) занимал не более сотен миллисекунд.

На некоторых машинах разработки, но не на всех (на всех машинах Windows 7) процесс десериализации - то есть вызов ReadObject (stream) занимает несколько минут и кажется блокирует всю машину, НО ТОЛЬКО ПРИ РАБОТЕ В ОТЛАДЧИКЕ в VS2008. Запуск кода конфигурации Debug вне отладчика не вызывает проблем.

Одна вещь, которая кажется подозрительной, состоит в том, что при включении остановки при исключениях вы можете видеть, что ReadObject () выдает много-много исключений System.FormatException, указывающих на то, что число имеет неправильный формат. Когда я отключаю «Просто мой код», тысячи из них выводятся на экран. Никто не останется без внимания. Это происходит как при обратном чтении из кэша, так и при десериализации при завершении вызова веб-службы для получения данных из службы WCF. ОДНАКО, такие же исключения происходят на моей машине для разработки ноутбуков, которая вообще не испытывает медлительности. И FWIW, мой ноутбук действительно старый, а мой рабочий стол - 4-ядерный, 6 ГБ ОЗУ.

Опять же, нет проблем, если только он не работает под отладчиком в VS2008. Кому-нибудь еще кажется это? Есть мысли?

Вот ссылка на сообщение об ошибке: https://connect.microsoft.com/VisualStudio/feedback/details/539609/very-slow-performance-deserializing-using-datacontractserializer-in-a-silverlight-application-only-in-debugger

РЕДАКТИРОВАТЬ: Теперь я знаю, откуда берутся исключения FormatException. Кажется, что они «по замыслу» - они происходят, когда у меня есть сериализируемые double, которые являются double.NaN, так что этот XML выглядит как NaN ... Кажется, что DCS пытается проанализировать значение как число, что не удается с исключением, а затем он ищет "NaN" et. и др. и обрабатывает их. Моя проблема не в том, что это не работает ... оно работает ... просто оно полностью калечит отладчик. Кто-нибудь знает, как настроить отладчик / vs2008sp1 для более эффективной обработки.

Ответы [ 5 ]

2 голосов
/ 08 марта 2010

cartden,

Возможно, вы захотите вместо этого переключиться на XMLSerializer.Вот что я определил с течением времени:

Классы XMLSerializer и DataContractSerializer предоставляют простые средства сериализации и десериализации графов объектов в и из XML.Ключевые отличия:1.XMLSerializer имеет гораздо меньшую полезную нагрузку, чем DCS, если вы используете [XmlAttribute] вместо [XmlElement]DCS всегда хранит значения как элементы2.DCS является «opt-in», а не «opt-out»С DCS вы явно отмечаете то, что хотите сериализовать с помощью [DataMember]С DCS вы можете сериализовать любое поле или свойство, даже если они помечены как защищенные или частныеС DCS вы можете использовать [IgnoreDataMember], чтобы сериализатор игнорировал определенные свойстваС помощью XMLSerializer открытые свойства сериализуются и требуют десериализации установщиковС XmlSerializer вы можете использовать [XmlIgnore], чтобы сериализатор игнорировал открытые свойства3.БУДЬТЕ ВНИМАТЕЛЬНЫ!DCS.ReadObject НЕ вызывает конструкторы во время десериализацииЕсли вам нужно выполнить инициализацию, DCS поддерживает следующие перехваты обратного вызова:[OnDeserializing], [OnDeserialized], [OnSerializing], [OnSerialized](также полезно для решения проблем с версиями)Если вам нужна возможность переключения между двумя сериализаторами, вы можете использовать оба набора атрибутов одновременно, как показано в:

[DataContract]
[XmlRoot]
    public class ProfilePerson : NotifyPropertyChanges
    {
[XmlAttribute]
[DataMember]
        public string FirstName { get { return m_FirstName; } set { SetProperty(ref m_FirstName, value); } }
        private string m_FirstName;
[XmlElement]
[DataMember]
        public PersonLocation Location { get { return m_Location; } set { SetProperty(ref m_Location, value); } }
        private PersonLocation m_Location = new PersonLocation(); // Should change over time
[XmlIgnore]
[IgnoreDataMember]
        public Profile ParentProfile { get { return m_ParentProfile; } set { SetProperty(ref m_ParentProfile, value); } }
        private Profile m_ParentProfile = null;

        public ProfilePerson()
        {
        }
    }

Также, проверьте мой класс Сериализатора, который может переключаться между ними:

using System;
using System.IO;
using System.Runtime.Serialization;
using System.Text;
using System.Xml;
using System.Xml.Serialization;

namespace ClassLibrary
{
    // Instantiate this class to serialize objects using either XmlSerializer or DataContractSerializer
    internal class Serializer
    {
        private readonly bool m_bDCS;

        internal Serializer(bool bDCS)
        {
            m_bDCS = bDCS;
        }

        internal TT Deserialize<TT>(string input)
        {
            MemoryStream stream = new MemoryStream(input.ToByteArray());
            if (m_bDCS)
            {
                DataContractSerializer dc = new DataContractSerializer(typeof(TT));
                return (TT)dc.ReadObject(stream);
            }
            else
            {
                XmlSerializer xs = new XmlSerializer(typeof(TT));
                return (TT)xs.Deserialize(stream);
            }
        }

        internal string Serialize<TT>(object obj)
        {
            MemoryStream stream = new MemoryStream();
            if (m_bDCS)
            {
                DataContractSerializer dc = new DataContractSerializer(typeof(TT));
                dc.WriteObject(stream, obj);
            }
            else
            {
                XmlSerializer xs = new XmlSerializer(typeof(TT));
                xs.Serialize(stream, obj);
            }

            // be aware that the Unicode Byte-Order Mark will be at the front of the string
            return stream.ToArray().ToUtfString();
        }

        internal string SerializeToString<TT>(object obj)
        {
            StringBuilder builder = new StringBuilder();
            XmlWriter xmlWriter = XmlWriter.Create(builder);
            if (m_bDCS)
            {
                DataContractSerializer dc = new DataContractSerializer(typeof(TT));
                dc.WriteObject(xmlWriter, obj);
            }
            else
            {
                XmlSerializer xs = new XmlSerializer(typeof(TT));
                xs.Serialize(xmlWriter, obj);
            }

            string xml = builder.ToString();
            xml = RegexHelper.ReplacePattern(xml, RegexHelper.WildcardToPattern("<?xml*>", WildcardSearch.Anywhere), string.Empty);
            xml = RegexHelper.ReplacePattern(xml, RegexHelper.WildcardToPattern(" xmlns:*\"*\"", WildcardSearch.Anywhere), string.Empty);
            xml = xml.Replace(Environment.NewLine + "  ", string.Empty);
            xml = xml.Replace(Environment.NewLine, string.Empty);
            return xml;
        }
    }
}
1 голос
/ 17 марта 2010

Окей - разобрался с коренной проблемой. Это было то, что я упоминал в РЕДАКТИРОВАТЬ к основному вопросу. Проблема заключалась в том, что в xml корректно сериализировались значения типа double с значением double.NaN. Я использовал эти значения для обозначения «na», когда знаменатель был 0D. Пример: ROE (Рентабельность собственного капитала = Чистый доход / Средний капитал), когда Средний капитал равен 0D, будет сериализовано как:

<ROE>NaN</ROE> 

Когда DCS пытался десериализовать его, очевидно, он сначала пытается прочитать число, а затем перехватывает исключение, когда это не удается, а затем обрабатывает NaN. Проблема в том, что в режиме DEBUG это создает большие накладные расходы.

Решение: Я изменил свойство на удвоенное? и установите его в ноль вместо NaN. Теперь все происходит мгновенно в режиме отладки. Спасибо всем за помощь.

1 голос
/ 16 марта 2010

Что касается проблемы с отладкой, пытались ли вы отключить помощника по исключениям? (Инструменты> Параметры> Отладка> Включить помощника по исключениям).

Другим пунктом должна быть обработка исключений в Debug> Exceptions: вы можете отключить необработанные пользователем вещи для CLR или снять только исключение System.FormatException.

1 голос
/ 16 марта 2010

Это предположение, но я думаю, что он работает медленно в режиме отладки, потому что для каждого исключения он выполняет некоторые действия, чтобы показать исключение в окне отладки и т. Д. Если вы работаете в режиме выпуска, эти дополнительные шаги не принимаются.

Я никогда не делал этого, поэтому я действительно не знаю, будет ли это работать, но пробовали ли вы просто установить одну сборку для запуска в режиме выпуска, в то время как все остальные настроены на отладку? Если я прав, это может решить вашу проблему. Если я не прав, то вы тратите только 1 или 2 минуты.

0 голосов
/ 03 ноября 2011

Попробуйте отключить некоторые дополнения IE. В моем случае панель инструментов LastPass убила мою отладку Silverlight. Мой компьютер зависал на несколько минут каждый раз, когда я взаимодействовал с Visual Studio после точки останова.

...