序列化/反序列化大型数据集

时间:2018-02-26 09:41:02

标签: c# dataset out-of-memory deserialization large-data

我有一个报告工具,可以将查询请求发送到服务器。在服务器完成查询之后,结果将被发送回请求报告工具。通信使用WCF完成。

存储在DataSet对象中的查询数据非常大,通常为100mb左右。

为了固定传输,我串行化(BinaryFormatter)并压缩DataSet。服务器和报告工具之间传输的对象是一个字节数组。

但是,在几次请求之后,报告工具在尝试反序列化DataSet时抛出OutOfMemoryException。当我打电话时抛出异常:

dataSet = (DataSet) formatter.Deserialize(dstream);

dstream是用于解压缩传输的压缩字节数组的DeflateStream。

当从流中创建字节数组时,在formatter.Deserialize的子调用中发生异常。

有没有其他方法的二进制序列化有更好的机制来防止这种异常?

实施

序列化和压缩DataSet的方法(由服务器使用)

public static byte[] Compress(DataSet dataSet)
{
    using (var input = new MemoryStream())
    {
        var binaryFormatter = new BinaryFormatter();
        binaryFormatter.Serialize(input, dataSet);

        using (var output = new MemoryStream())
        {
            using (var compressor = new DeflateStream(output, CompressionLevel.Optimal))
            {
                input.Position = 0;

                var buffer = new byte[1024];

                int read;

                while ((read = input.Read(buffer, 0, buffer.Length)) > 0)
                    compressor.Write(buffer, 0, read);

                compressor.Close();

                return output.ToArray();
            }
        }
    }
}

解压缩和反序列化DataSet的方法(由报告工具使用)

public static DataSet Decompress(byte[] data)
{
    DataSet dataSet;

    using (var input = new MemoryStream(data))
    {
        using (var dstream = new DeflateStream(input, CompressionMode.Decompress))
        {
            var formatter = new BinaryFormatter();
            dataSet = (DataSet) formatter.Deserialize(dstream);
        }
    }

    return dataSet;
}

堆栈跟踪:

at System.Array.InternalCreate(Void* elementType, Int32 rank, Int32* pLengths, Int32* pLowerBounds)
at System.Array.CreateInstance(Type elementType, Int32 length)
at System.Array.UnsafeCreateInstance(Type elementType, Int32 length)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseArray(ParseRecord pr)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.ParseObject(ParseRecord pr)
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Parse(ParseRecord pr)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.ReadArray(BinaryHeaderEnum binaryHeaderEnum)
at System.Runtime.Serialization.Formatters.Binary.__BinaryParser.Run()
at System.Runtime.Serialization.Formatters.Binary.ObjectReader.Deserialize(HeaderHandler handler, __BinaryParser serParser, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream, HeaderHandler handler, Boolean fCheck, Boolean isCrossAppDomain, IMethodCallMessage methodCallMessage)
at System.Runtime.Serialization.Formatters.Binary.BinaryFormatter.Deserialize(Stream serializationStream)
at DRX.PTClientMonitoring.Infrastructure.Helper.DataSetCompressor.Decompress(Byte[] data) in c:\_develop\PTClientMonitoringTool\PTClientMonitoringTool\Source\DRX.PTClientMonitoring.Infrastructure\Helper\DataSetCompressor.cs:line 51
at DRX.PTClientMonitoring.Reporting.ViewModels.ShellViewModel.<>c__DisplayClassf.<ExecudeDefinedQuery>b__4() in c:\_develop\PTClientMonitoringTool\PTClientMonitoringTool\Source\DRX.PTClientMonitoring.Reporting\ViewModels\ShellViewModel.cs:line 347

1 个答案:

答案 0 :(得分:2)

在序列化之前,请设置:

yourDataSet.RemotingFormat = SerializationFormat.Binary;

这应该有很多帮助。即使使用BinaryFormatter ,默认的也是xml。

但请注意,DataSetDataTable 本身不是优化的理想选择。有许多优秀的序列化工具可以更好地更好地打包您的数据,但它们不变,需要强类型模型,即List<SomeSpecificType>其中SomeSpecificType是POCO / DTO课程。即使是WCF也只能容忍DataTable / DataSet。因此,如果您可以摆脱对DataTable / DataSet的依赖:我强烈建议您这样做。

另一种选择是Stream 的形式发送数据。我非常确定WCF原生支持这种做法,但理论上这可以让你有一个实际上更大的{{1>}( Stream)。作为一个便宜的选项,您可以使用临时文件作为临时区域,但如果可行,您可以调查自定义内存中的流,将多个缓冲区拼接在一起。