65.9K
CodeProject 正在变化。 阅读更多。
Home

使用 GZip 压缩 DataTable 序列化

starIconstarIconstarIconemptyStarIconemptyStarIcon

3.00/5 (3投票s)

2008年9月17日

CPOL

2分钟阅读

viewsIcon

27953

downloadIcon

477

使用这个FastDataTable,您可以快速且良好地压缩序列化您的数据。

引言

在我们的项目工作中,这是一个三层业务解决方案,其中包含大量数据,在服务器和客户端之间流动,我们(指的是我们的开发人员部门)面临着压缩从服务器传输到客户端应用程序的数据的问题。 主要问题是序列化和反序列化的过程,因为它隐藏在 ADO.NET 的核心深处,并且很难从您的代码中更改它。 有一段时间我们使用了 FastDataSet 实现,其中序列化过程被完全重写。 长期以来,这是我们解决问题的最佳方案,但突然我们注意到,在某些情况下,当通过局域网将数据传递给报表生成器时,数据会损坏。 我们唯一的办法是理解如何使这种序列化对我们的数据简单、快速且无害。 看来我们做到了,所以我想和您分享一下这方面的知识。

背景

此代码的主要重点是使用内部 DataTable 方法,例如 DeserializeTableSchemaDeserializeTableDataResetIndexes。 DataTable 类在反序列化中使用这三个函数。 我们也是如此,但就在使用这些函数之前,我们需要解压缩保存在 SerializationInfo 类中的数据。 序列化数据时,首先使用默认机制,然后在压缩它 - 仅此而已。 由于某些限制

Using the Code

所以,代码的主要部分是:方法 GetObjectData,这是格式化程序用来将对象数据序列化到 SerializationInfo 对象中的方法。 此对象仅包含原始对象的所有字段、这些字段的类型及其名称。 我们在那里做了一些技巧 - 将真实数据获取到临时信息中,然后使用反射进入其私有字段,对其进行压缩,然后将这些数据放入真实的序列化信息中。

public override void GetObjectData(SerializationInfo info, StreamingContext context) {
    SerializationInfo zipInfo = new SerializationInfo(typeof(FastDataTable),
        new FormatterConverter());
    base.GetObjectData(zipInfo, context);

    FieldInfo fiData = typeof(SerializationInfo).GetField("m_data",
        BindingFlags.NonPublic | BindingFlags.Instance);
    FieldInfo fiMembers = typeof(SerializationInfo).GetField("m_members",
        BindingFlags.NonPublic | BindingFlags.Instance);
    FieldInfo fiTypes = typeof(SerializationInfo).GetField("m_types",
        BindingFlags.NonPublic | BindingFlags.Instance);
    object[] data = (object[])fiData.GetValue(zipInfo);
    string[] members = (string[])fiMembers.GetValue(zipInfo);
    Type[] types = (Type[])fiTypes.GetValue(zipInfo);

    IFormatter formatter = new BinaryFormatter();
    using(MemoryStream stream = new MemoryStream()) {
        formatter.Serialize(stream, data);
        formatter.Serialize(stream, members);
        formatter.Serialize(stream, types);
        formatter.Serialize(stream, zipInfo.MemberCount);

        using(MemoryStream streamZip = new MemoryStream()) {
            stream.Position = 0;
            byte[] arr = null;
            if(useCompression && stream.Length > compressThreshold) {
                Compress(stream, streamZip);
                arr = streamZip.ToArray();
            } else {
                arr = stream.ToArray();
            }

            info.AddValue("chunk", arr);
            info.AddValue("compressed",
                useCompression && stream.Length > compressThreshold);
        }
    }
}

第二个重要的方法是反序列化的重写构造函数。 在那里,我们解压缩原始的序列化信息,将真实数据放入信息对象中,然后再次使用反射,调用 DataTable 类用来反序列化表的方法。

protected FastDataTable(SerializationInfo info, StreamingContext context) {
    MethodInfo miDeTS = typeof(FastDataTable).GetMethod(
        "DeserializeTableSchema", 
        BindingFlags.NonPublic | BindingFlags.Instance);
    MethodInfo miDeTD = typeof(FastDataTable).GetMethod("DeserializeTableData",
        BindingFlags.NonPublic | BindingFlags.Instance);
    MethodInfo miResIn = typeof(FastDataTable).GetMethod("ResetIndexes",
        BindingFlags.NonPublic | BindingFlags.Instance);

    using(MemoryStream stream = new MemoryStream()) {
        byte[] bytes = (byte[])info.GetValue("chunk", typeof(byte[]));
        useCompression = (bool)info.GetValue("compressed", typeof(bool));

        compressedSize = bytes.Length;

        if(useCompression) {
            using(MemoryStream streamUnzip = new MemoryStream(bytes)) {
                Decompress(streamUnzip, stream);
            }
        } else {
            stream.Write(bytes, 0, bytes.Length);
        }

        stream.Position = 0;
        originalSize = (int)stream.Length;
        IFormatter formatter = new BinaryFormatter();

        FieldInfo fiData = typeof(SerializationInfo).GetField(
            "m_data", BindingFlags.NonPublic | BindingFlags.Instance);
        FieldInfo fiMembers = typeof(SerializationInfo).GetField(
            "m_members", BindingFlags.NonPublic | BindingFlags.Instance);
        FieldInfo fiTypes = typeof(SerializationInfo).GetField("m_types",
            BindingFlags.NonPublic | BindingFlags.Instance);
        FieldInfo fiCurrMember = typeof(SerializationInfo).GetField(
            "m_currMember", BindingFlags.NonPublic | BindingFlags.Instance);

        object[] data = (object[])formatter.Deserialize(stream);
        string[] members = (string[])formatter.Deserialize(stream);
        Type[] types = (Type[])formatter.Deserialize(stream);
        int curMember = (int)formatter.Deserialize(stream);

        fiData.SetValue(info, data);
        fiMembers.SetValue(info, members);
        fiTypes.SetValue(info, types);
        fiCurrMember.SetValue(info, curMember);
    }


    miDeTS.Invoke(this, new object[] { info, context, true });
    miDeTD.Invoke(this, new object[] { info, context, 0 });
    miResIn.Invoke(this, new object[] { });
}

所以,这就是我们使用的主要功能。 另外,不要忘记将 RemotingFormat 设置为 SerializationFormat.Binary

关注点

我们在编写此代码时面临的最大问题是,在 C# 中,我们无法在构造函数的任何地方调用基类构造函数 - 只能在开头调用。 因此,我们被迫使用所有这些反射东西。 .NET Reflector 在理解框架代码结构方面提供了很大的帮助 - 这是一个非常酷的工具!

历史

还没有 :)

© . All rights reserved.