我有一个报告工具,可以将查询请求发送到服务器。在服务器完成查询之后,结果将被发送回请求报告工具。通信使用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
答案 0 :(得分:2)
在序列化之前,请设置:
yourDataSet.RemotingFormat = SerializationFormat.Binary;
这应该有很多帮助。即使使用BinaryFormatter
,默认的也是xml。
但请注意,DataSet
和DataTable
本身不是优化的理想选择。有许多优秀的序列化工具可以更好地>更好地打包您的数据,但它们不变,需要强类型模型,即List<SomeSpecificType>
其中SomeSpecificType
是POCO / DTO课程。即使是WCF也只能容忍DataTable
/ DataSet
。因此,如果您可以摆脱对DataTable
/ DataSet
的依赖:我强烈建议您这样做。
另一种选择是以Stream
的形式发送数据。我非常确定WCF原生支持这种做法,但理论上这可以让你有一个实际上更大的{{1>}(不 Stream
)。作为一个便宜的选项,您可以使用临时文件作为临时区域,但如果可行,您可以调查自定义内存中的流,将多个缓冲区拼接在一起。