在 Silverlight 应用程序中使用 datacontractserializer 进行反序列化性能非常慢

发布于 2024-08-23 19:09:16 字数 1386 浏览 7 评论 0 原文

情况如下:

Silverlight 3 应用程序访问 asp.net 托管的 WCF 服务来获取要在网格中显示的项目列表。一旦列表被传送到客户端,它就会被缓存在IsolatedStorage 中。这是通过使用 DataContractSerializer 将所有这些对象序列化到一个流中,然后对该流进行压缩和加密来完成的。当应用程序重新启动时,它首先从缓存加载(与上面的过程相反),并使用 DataContractSerializer.ReadObject() 方法反序列化对象。所有这些在所有场景下都运行良好,直到最近,整个“从缓存加载”路径(解密/解压缩/反序列化)最多需要数百毫秒。

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

看起来可疑的一件事是,当您打开“停止异常”时,您可以看到 ReadObject() 抛出许多许多 System.FormatException,表明数字的格式不正确。当我关闭“仅我的代码”时,成千上万的代码会转储到屏幕上。没有一个是未经处理的。这些发生在从缓存读回以及 Web 服务调用结束时进行反序列化以从 WCF 服务获取数据时。然而,这些相同的异常发生在我的笔记本电脑开发机器上,根本没有遇到缓慢的情况。 FWIW,我的笔记本电脑真的很旧,而我的台式机是 4 核、6GB RAM 的野兽。

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

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

编辑:我现在知道 FormatExceptions 来自哪里。似乎它们是“设计使然”——当我序列化双精度数时,它们会发生。NaN 使得 xml 看起来像 NaN...似乎 DCS 试图将值解析为数字,但失败了有一个例外,然后它会查找“NaN”等。等人。并处理它们。我的问题不是这不起作用……它确实起作用……只是它完全削弱了调试器。有谁知道如何配置 debugger/vs2008sp1 来更有效地处理这个问题。

Here is the situation:

Silverlight 3 Application hits an asp.net hosted WCF service to get a list of items to display in a grid. Once the list is brought down to the client it is cached in IsolatedStorage. This is done by using the DataContractSerializer to serialize all of these objects to a stream which is then zipped and then encrypted. When the application is relaunched, it first loads from the cache (reversing the process above) and the deserializes the objects using the DataContractSerializer.ReadObject() method. All of this was working wonderfully under all scenarios until recently with the entire "load from cache" path (decrypt/unzip/deserialize) taking hundreds of milliseconds at most.

On some development machines but not all (all machines Windows 7) the deserialize process - that is the call to ReadObject(stream) takes several minutes an seems to lock up the entire machine BUT ONLY WHEN RUNNING IN THE DEBUGGER in VS2008. Running the Debug configuration code outside the debugger has no problem.

One thing that seems to look suspicious is that when you turn on stop on Exceptions, you can see that the ReadObject() throws many, many System.FormatException's indicating that a number was not in the correct format. When I turn off "Just My Code" thousands of these get dumped to the screen. None go unhandled. These occur both on the read back from the cache AND on a deserialization at the conclusion of a web service call to get the data from the WCF Service. HOWEVER, these same exceptions occur on my laptop development machine that does not experience the slowness at all. And FWIW, my laptop is really old and my desktop is a 4 core, 6GB RAM beast.

Again, no problems unless running under the debugger in VS2008. Anyone else seem this? Any thoughts?

Here is the bug report link: https://connect.microsoft.com/VisualStudio/feedback/details/539609/very-slow-performance-deserializing-using-datacontractserializer-in-a-silverlight-application-only-in-debugger

EDIT: I now know where the FormatExceptions are coming from. It seems that they are "by design" - they occur when when I have doubles being serialized that are double.NaN so that that xml looks like NaN...It seems that the DCS tries to parse the value as a number, that fails with an exception and then it looks for "NaN" et. al. and handles them. My problem is not that this does not work...it does...it is just that it completely cripples the debugger. Does anyone know how to configure the debugger/vs2008sp1 to handle this more efficiently.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

嘿看小鸭子会跑 2024-08-30 19:09:16

cartden,

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

XMLSerializer 和 DataContractSerializer 类提供了一种将对象图序列化到 XML 和反序列化 XML 的简单方法。

主要区别是:
1.
如果使用 [XmlAttribute] 而不是 [XmlElement],XMLSerializer 的有效负载比 DCS 小得多
DCS 始终将值存储为元素
2.
DCS 是“选择加入”而不是“选择退出”
使用 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;
        }
    }
}

cartden,

You may want to consider switching over to XMLSerializer instead. Here is what I have determined over time:

The XMLSerializer and DataContractSerializer classes provides a simple means of serializing and deserializing object graphs to and from XML.

The key differences are:
1.
XMLSerializer has much smaller payload than DCS if you use [XmlAttribute] instead of [XmlElement]
DCS always store values as elements
2.
DCS is "opt-in" rather than "opt-out"
With DCS you explicitly mark what you want to serialize with [DataMember]
With DCS you can serialize any field or property, even if they are marked protected or private
With DCS you can use [IgnoreDataMember] to have the serializer ignore certain properties
With XMLSerializer public properties are serialized, and need setters to be deserialized
With XmlSerializer you can use [XmlIgnore] to have the serializer ignore public properties
3.
BE AWARE! DCS.ReadObject DOES NOT call constructors during deserialization
If you need to perform initialization, DCS supports the following callback hooks:
[OnDeserializing], [OnDeserialized], [OnSerializing], [OnSerialized]
(also useful for handling versioning issues)

If you want the ability to switch between the two serializers, you can use both sets of attributes simultaneously, as in:

[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()
        {
        }
    }

Also, check out my Serializer class that can switch between the two:

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;
        }
    }
}
白衬杉格子梦 2024-08-30 19:09:16

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

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

This is a guess, but I think it is running slow in debug mode because for every exception, it is performing some actions to show the exception in the debug window, etc. If you are running in release mode, these extra steps are not taken.

I've never done this, so I really don't know id it would work, but have you tried just setting that one assembly to run in release mode while all others are set to debug? If I'm right, it may solve your problem. If I'm wrong, then you only waste 1 or 2 minutes.

深海夜未眠 2024-08-30 19:09:16

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

还有一点应该是Debug>中的异常处理。异常:您可以禁用 CLR 的用户未处理的内容,或者仅取消选中 System.FormatException 异常。

About your debugging problem, have you tried to disable the exception assistant ? (Tools > Options > Debugging > Enable the exception assistant).

Another point should be the exception handling in Debug > Exceptions : you can disable the user-unhandled stuff for the CLR or only uncheck the System.FormatException exception.

情释 2024-08-30 19:09:16

好的 - 我找到了根本问题。这是我在主要问题的编辑中提到的。问题在于,在 xml 中,它正确序列化了值为 double.NaN 的双精度数。当分母为 0D 时,我使用这些值来表示“na”。示例:当平均股本为 0D 时,ROE(股本回报率 = 净利润 / 平均股本)将被序列化为:

<ROE>NaN</ROE> 

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

解决办法:我把属性改成double了?并将其设置为 null 而不是 NaN。现在一切都会在调试模式下立即发生。感谢大家的帮助。

Ok - I figured out the root issue. It was what I alluded to in the EDIT to the main question. The problem was that in the xml, it was correctly serializing doubles that had a value of double.NaN. I was using these values to indicate "na" for when the denominator was 0D. Example: ROE (Return on Equity = Net Income / Average Equity) when Average Equity is 0D would be serialized as:

<ROE>NaN</ROE> 

When the DCS tried to de-serialize it, evidently it first tries to read the number and then catches the exception when that fails and then handles the NaN. The problem is that this seems to generate a lot of overhead when in DEBUG mode.

Solution: I changed the property to double? and set it to null instead of NaN. Everything now happens instantly in DEBUG mode now. Thanks to all for your help.

最舍不得你 2024-08-30 19:09:16

尝试禁用一些 IE 插件。就我而言,LastPass 工具栏终止了我的 Silverlight 调试。每次在断点后与 Visual Studio 交互时,我的计算机都会冻结几分钟。

Try disabling some IE addons. In my case, the LastPass toolbar killed my Silverlight debugging. My computer would freeze for minutes each time I interacted with Visual Studio after a breakpoint.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文