代码之家  ›  专栏  ›  技术社区  ›  caryden

在Silverlight应用程序中使用DataContractSerializer进行反序列化的速度非常慢

  •  3
  • caryden  · 技术社区  · 14 年前

    情况如下:

    Silverlight 3应用程序命中ASP.NET承载的WCF服务以获取要在网格中显示的项目列表。一旦将列表放到客户机上,它就会缓存在isolatedStorage中。这是通过使用DataContractSerializer将所有这些对象序列化为流来完成的,然后对该流进行压缩和加密。当重新启动应用程序时,它首先从缓存加载(颠倒上面的过程),然后使用dataContractSerializer.readObject()方法反序列化对象。直到最近,所有这些在所有场景下都非常有效,整个“从缓存加载”路径(解密/解压/反序列化)最多花费数百毫秒。

    在一些开发机器上,但不是所有的(所有的机器Windows7),反序列化过程-即对readObject(流)的调用需要几分钟时间-似乎锁定了整个机器,但仅当在VS2008中的调试器中运行时。在调试器外部运行调试配置代码没有问题。

    一件看起来可疑的事情是,当您打开stop on exceptions时,您可以看到readObject()抛出了许多system.formatException,这表示一个数字的格式不正确。当我关闭“仅我的代码”时,成千上万的代码会被丢弃到屏幕上。无转到未处理。这两种情况都发生在从缓存中读取数据和从WCF服务获取数据的Web服务调用结束时的反序列化上。然而,在我的笔记本电脑开发机器上也出现了同样的例外,它根本不会经历任何缓慢。而fwiw,我的笔记本电脑真的很旧,我的桌面是一个4核,6GB内存的野兽。

    同样,除非在VS2008中的调试器下运行,否则没有问题。其他人看起来像这样吗?有什么想法吗?

    以下是错误报告链接: https://connect.microsoft.com/VisualStudio/feedback/details/539609/very-slow-performance-deserializing-using-datacontractserializer-in-a-silverlight-application-only-in-debugger

    编辑:我现在知道FormatExceptions是从哪里来的。似乎它们是“按设计的”—当我将double.nan的double序列化,使XML看起来像NaN时,就会发生这种情况……看起来,DCS试图将值解析为一个数字,但由于出现异常而失败,然后查找“NaN”等。处理它们。我的问题不是这不起作用……它起作用……只是它完全削弱了调试器。有人知道如何配置调试器/vs2008sp1以更有效地处理这个问题吗?

    5 回复  |  直到 13 年前
        1
  •  2
  •   Gabriel McAdams    14 年前

    卡登

    您可能需要考虑切换到XmlSerializer。以下是我随着时间的推移所决定的:

    XmlSerializer和DataContractSerializer类提供了一种简单的方法,可以在XML之间对对象图进行序列化和反序列化。

    主要区别在于:
    1.
    如果使用[xmlAttribute]而不是[xmlElement],则xmlSerializer的有效负载比dcs小得多。
    DCS始终将值存储为元素
    2.第2条。
    DCS是“选择加入”而不是“选择退出”
    使用dcs,可以显式标记要用[datamember]序列化的内容。
    使用dcs,您可以序列化任何字段或属性,即使它们被标记为受保护或私有
    使用dcs,可以使用[ignoredatamember]使序列化程序忽略某些属性。
    使用XmlSerializer对公共属性进行序列化,并且需要对setter进行反序列化
    使用XmlSerializer,可以使用[XmlIgnore]使序列化程序忽略公共属性
    三。
    注意!dcs.readObject在反序列化期间不调用构造函数
    如果需要执行初始化,则DCS支持以下回调挂钩:
    [反序列化]、[反序列化]、[反序列化]、[反序列化]、[反序列化]
    (对于处理版本控制问题也很有用)

    如果希望能够在两个序列化程序之间切换,则可以同时使用这两组属性,如:

    [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;
            }
        }
    }
    
        2
  •  1
  •   Gabriel McAdams    14 年前

    这是一个猜测,但我认为它在调试模式下运行缓慢,因为对于每个异常,它都在执行一些操作以在调试窗口中显示异常,等等。如果您在发布模式下运行,则不会采取这些额外的步骤。

    我从来没有这样做过,所以我真的不知道ID它会起作用,但是您是否尝试过将一个程序集设置为在发布模式下运行,而将所有其他程序集设置为调试?如果我是对的,它可以解决你的问题。如果我错了,那你就只浪费1到2分钟。

        3
  •  1
  •   JoeBilly    14 年前

    关于调试问题,是否尝试禁用异常助手?(工具>选项>调试>启用异常助手)。

    另一点应该是调试中的异常处理:您可以为clr禁用用户未处理的内容,或者只取消选中System.FormatException异常。

        4
  •  1
  •   caryden    14 年前

    好吧-我解决了根本问题。这就是我在编辑中提到的主要问题。问题是,在XML中,正确地序列化double,其值为double.nan。我使用这些值来表示分母为0d时的“na”。例如:当平均股本为0d时,roe(股本回报率=净收入/平均股本)将序列化为:

    <ROE>NaN</ROE> 
    

    当分布式控制系统试图对其进行反序列化时,显然它首先尝试读取数字,然后在失败时捕获异常,然后处理NaN。问题是,在调试模式下,这似乎会产生大量开销。

    解决方案:我将属性更改为Double?并将其设置为空而不是NaN。现在一切都在调试模式下立即发生。谢谢大家的帮助。

        5
  •  0
  •   Josh Mouch    13 年前

    尝试禁用一些IE加载项。在我的例子中,LastPass工具栏终止了我的Silverlight调试。每次我在一个断点后与Visual Studio交互时,我的计算机都会冻结几分钟。