积极答复者
请教如何通过WCF传输大数据量数据

问题
-
因为有个项目需要在客户端导入Excel,我的做法是:先把Excel文件读入成DataSet,然后转成XML,通过WCF传给后台,再还原成DataSet后更新数据库。
现在遇到的问题是,数据量比较小的Excel文件通过这种方式可以导入成功,但是数据量大的Excel文件,总是导入失败。
例如手头有个Excel文件,列从A~S,总共2750行,Excel文件大小854K,转成XML文件,xml.Length=1307341,调用WCF后提示以下错误:
接收对 http://<IP>/Services/InitializeService.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。有关详细信息,请参阅服务器日志。
而较小文件(Excel文件内容500行左右)则不会出现此问题,我修改过配置文件,但没有效果:
<bindings>
<wsHttpBinding>
<binding name="DefaultBinding" closeTimeout="00:10:00"
openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="52428800" maxReceivedMessageSize="6553600"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192000" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
</wsHttpBinding>
</bindings>
请问应如何处理,谢谢。
答案
-
就是直接把DataSet 类型作为参数直接传递给服务端
WCF默认支持这么做,直接传Datatable不行。
你看一下 “服务引用设置”中你选的集合类型是什么,我选的是System.Array
字典集合类型是默认第一项 System.Collections.Generic.Dictionary- 已标记为答案 Allen Chen - MSFTModerator 2009年9月25日 3:17
-
谢谢回复
To Frank Xu Lei:
我做了进一步测试,发现虽然我的DataSet转成XML后,xml.Length=1307341,但是因为通过WCF传输,所以会被再次序列化(把我XML中的尖括号替换掉),实际的大小约是1900000。但我试过把配置改成如下方式,所有的配置项都调大,但效果依旧:
<binding name="DefaultBinding" closeTimeout="00:10:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="52428800" maxReceivedMessageSize="6553600"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192000" maxArrayLength="16384000"
maxBytesPerRead="409600" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
To 云在起时:
没试过你说的方法,有无例子?
谢谢。
云在起时,的说法也不错。
1,你可以尝试直接传递Dataset,或者使用Datatable看看,我记得也可以,但是客户端要是.net平台。
这个是参考文章。有代码
WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据
2.至于自定义List<contract>来传递你的Excel数据,你说麻烦,其实这个是最高效的,任何.NET 的类定义一般都是考虑很多方面,比如DataSet,很多属性或者不必要的字段,初始化都会暂用内存。
3.WCF消息编码的例子网上也有,我自己的测试代码属于公司的项目,所以不能外出。
不过我会给你参考意见。
你可以先试验一下第一个方式。
Frank Xu Lei--谦卑若愚,好学若饥
专注于.NET平台下分布式应用系统开发和企业应用系统集成
Focus on Distributed Applications Development and EAI based on .NET
欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum- 已标记为答案 Allen Chen - MSFTModerator 2009年9月25日 3:17
全部回复
-
补充一下,以下是报错的完整日志信息:
<Exception>
<Description>An exception of type 'System.ServiceModel.CommunicationException' occurred and was caught.</Description>
<DateTime>2009-09-18 18:12:08Z</DateTime>
<ExceptionType>System.ServiceModel.CommunicationException, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>接收对 http://<IP>/Services/InitializeService.svc 的 HTTP 响应时发生错误。这可能是由于服务终结点绑定未使用 HTTP 协议造成的。这还可能是由于服务器中止了 HTTP 请求上下文(可能由于服务关闭)所致。有关详细信息,请参阅服务器日志。</Message>
<Source>mscorlib</Source>
<HelpLink />
<Property name="Data">System.Collections.ListDictionaryInternal</Property>
<Property name="TargetSite">Void HandleReturnMessage(System.Runtime.Remoting.Messaging.IMessage, System.Runtime.Remoting.Messaging.IMessage)</Property>
<StackTrace>
Server stack trace:
在 System.ServiceModel.Channels.HttpChannelUtilities.ProcessGetResponseWebException(WebException webException, HttpWebRequest request, HttpAbortReason abortReason)
在 System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)
在 System.ServiceModel.Channels.RequestChannel.Request(Message message, TimeSpan timeout)
在 System.ServiceModel.Channels.ClientReliableChannelBinder`1.RequestClientReliableChannelBinder`1.OnRequest(TRequestChannel channel, Message message, TimeSpan timeout, MaskingMode maskingMode)
在 System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout, MaskingMode maskingMode)
在 System.ServiceModel.Channels.ClientReliableChannelBinder`1.Request(Message message, TimeSpan timeout)
在 System.ServiceModel.Security.SecuritySessionClientSettings`1.SecurityRequestSessionChannel.Request(Message message, TimeSpan timeout)
在 System.ServiceModel.Dispatcher.RequestChannelBinder.Request(Message message, TimeSpan timeout)
在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs, TimeSpan timeout)
在 System.ServiceModel.Channels.ServiceChannel.Call(String action, Boolean oneway, ProxyOperationRuntime operation, Object[] ins, Object[] outs)
在 System.ServiceModel.Channels.ServiceChannelProxy.InvokeService(IMethodCallMessage methodCall, ProxyOperationRuntime operation)
在 System.ServiceModel.Channels.ServiceChannelProxy.Invoke(IMessage message)Exception rethrown at [0]:
在 System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
在 System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
在 Company.Security.Proxy.IInitializeService.AccessLogger(String methodName, String currentModule, String switchPort, String languageID, String currentUser, String password, String businessType, String businessKey, String[] dataObjects, String updateMethod, Int32 currentPage, Int32 pageSize)
在 Company.Security.Proxy.InitializeServiceClient.AccessLogger(String methodName, String currentModule, String switchPort, String languageID, String currentUser, String password, String businessType, String businessKey, String[] dataObjects, String updateMethod, Int32 currentPage, Int32 pageSize)
在 Company.Security.Router.ExternalMethod.CallUpdateMethod(String currentModule, String switchPort, String languageID, String currentUser, String password, String businessType, String businessKey, IList updateRecords, String updateMethod)</StackTrace>
<additionalInfo>
<info name="MachineName" value="DEV008" />
<info name="TimeStamp" value="2009/9/18 10:12:08" />
<info name="FullName" value="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a" />
<info name="AppDomainName" value="Company.SCMAX.Portal.WinForm.vshost.exe" />
<info name="ThreadIdentity" value="" />
<info name="WindowsIdentity" value="Company\User" />
</additionalInfo>
<InnerException>
<ExceptionType>System.Net.WebException, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>基础连接已经关闭: 接收时发生错误。</Message>
<Source>System</Source>
<HelpLink />
<Property name="Status">ReceiveFailure</Property>
<Property name="Response"><undefined value></Property>
<Property name="Data">System.Collections.ListDictionaryInternal</Property>
<Property name="TargetSite">System.Net.WebResponse GetResponse()</Property>
<StackTrace> 在 System.Net.HttpWebRequest.GetResponse()
在 System.ServiceModel.Channels.HttpChannelFactory.HttpRequestChannel.HttpChannelRequest.WaitForReply(TimeSpan timeout)</StackTrace>
<InnerException>
<ExceptionType>System.IO.IOException, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>无法从传输连接中读取数据: 远程主机强迫关闭了一个现有的连接。</Message>
<Source>System</Source>
<HelpLink />
<Property name="Data">System.Collections.ListDictionaryInternal</Property>
<Property name="TargetSite">Int32 Read(Byte[], Int32, Int32)</Property>
<StackTrace> 在 System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)
在 System.Net.PooledStream.Read(Byte[] buffer, Int32 offset, Int32 size)
在 System.Net.Connection.SyncRead(HttpWebRequest request, Boolean userRetrievedStream, Boolean probeRead)</StackTrace>
<InnerException>
<ExceptionType>System.Net.Sockets.SocketException, System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>远程主机强迫关闭了一个现有的连接。</Message>
<Source>System</Source>
<HelpLink />
<Property name="ErrorCode">10054</Property>
<Property name="SocketErrorCode">ConnectionReset</Property>
<Property name="NativeErrorCode">10054</Property>
<Property name="Data">System.Collections.ListDictionaryInternal</Property>
<Property name="TargetSite">Int32 Receive(Byte[], Int32, Int32, System.Net.Sockets.SocketFlags)</Property>
<StackTrace> 在 System.Net.Sockets.Socket.Receive(Byte[] buffer, Int32 offset, Int32 size, SocketFlags socketFlags)
在 System.Net.Sockets.NetworkStream.Read(Byte[] buffer, Int32 offset, Int32 size)</StackTrace>
</InnerException>
</InnerException>
</InnerException>
</Exception> -
Hi,
你描述的情况来看,不是很大的文件,我们曾经在安全模式下测试过20m的文件传输,都没有问题。
从你给出的描述来说,效率确实不高,错误很可能于此有关系。
不要DAtaset,这个效率太低,而且你的序列化和反序列化,可以由WCF来完成。
建议你自己定义数据契约,然后使用list<mycontract>来存放读取的excel文件里的数据。
这样会节省很多内存,其次也提高传输效率。
另外你可以考虑启用流模式,传输,然后考虑MTOM编码,另外注意配置文件里消息的长度设置。这个你可以参考一下:WCF分布式开发步步为赢(11):WCF流处理(Streaming)机制
有问题在交流~
Frank Xu Lei--谦卑若愚,好学若饥
专注于.NET平台下分布式应用系统开发和企业应用系统集成
Focus on Distributed Applications Development and EAI based on .NET
欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum -
Hi,
不要客气
你尝试把 <readerQuotas maxDepth="32" maxStringContentLength="8192000" maxArrayLength="16384"
maxArrayLength 的值修改的大点,很可能和传输有关系。一般WCF的错误都会引起通道异常关闭。
看看这样能不能解决问题。
Frank Xu Lei--谦卑若愚,好学若饥
专注于.NET平台下分布式应用系统开发和企业应用系统集成
Focus on Distributed Applications Development and EAI based on .NET
欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum -
谢谢回复
To Frank Xu Lei:
我做了进一步测试,发现虽然我的DataSet转成XML后,xml.Length=1307341,但是因为通过WCF传输,所以会被再次序列化(把我XML中的尖括号替换掉),实际的大小约是1900000。但我试过把配置改成如下方式,所有的配置项都调大,但效果依旧:
<binding name="DefaultBinding" closeTimeout="00:10:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="52428800" maxReceivedMessageSize="6553600"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192000" maxArrayLength="16384000"
maxBytesPerRead="409600" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
To 云在起时:
没试过你说的方法,有无例子?
谢谢。 -
补充一点,我服务端方法接收的参数类型是IList,如:
public void CallUpdateMethod(IList updateRecords);
然后客户端提交的参数值是new string[] { xml },如:
ServiceClient client = new ServiceClient();// 将上传的Excel转成DataSet(注:Converter是个自定义类库)。
DataSet ds = Converter.ConvertExcelToDataSet(@"D:\Downloads\导入模板.xls");if (ds != null && ds.Tables.Count > 0)
{
// 将DataSet序列化成XML字符串,通过Service传递到服务端业务逻辑层。
string xml = Converter.ConvertDataTableToXml(ds.Tables[0]);
client.CallUpdateMethod(new string[] { xml });
}
…… -
就是直接把DataSet 类型作为参数直接传递给服务端
WCF默认支持这么做,直接传Datatable不行。
你看一下 “服务引用设置”中你选的集合类型是什么,我选的是System.Array
字典集合类型是默认第一项 System.Collections.Generic.Dictionary- 已标记为答案 Allen Chen - MSFTModerator 2009年9月25日 3:17
-
谢谢回复
To Frank Xu Lei:
我做了进一步测试,发现虽然我的DataSet转成XML后,xml.Length=1307341,但是因为通过WCF传输,所以会被再次序列化(把我XML中的尖括号替换掉),实际的大小约是1900000。但我试过把配置改成如下方式,所有的配置项都调大,但效果依旧:
<binding name="DefaultBinding" closeTimeout="00:10:00"
openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00"
bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
maxBufferPoolSize="52428800" maxReceivedMessageSize="6553600"
messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
allowCookies="false">
<readerQuotas maxDepth="32" maxStringContentLength="8192000" maxArrayLength="16384000"
maxBytesPerRead="409600" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="00:10:00" enabled="false" />
<security mode="Message">
<transport clientCredentialType="Windows" proxyCredentialType="None" realm="" />
<message clientCredentialType="Windows" negotiateServiceCredential="true" algorithmSuite="Default" establishSecurityContext="true" />
</security>
</binding>
To 云在起时:
没试过你说的方法,有无例子?
谢谢。
云在起时,的说法也不错。
1,你可以尝试直接传递Dataset,或者使用Datatable看看,我记得也可以,但是客户端要是.net平台。
这个是参考文章。有代码
WCF分布式开发步步为赢(8):使用数据集(DataSet)、数据表(DataTable)、集合(Collection)传递数据
2.至于自定义List<contract>来传递你的Excel数据,你说麻烦,其实这个是最高效的,任何.NET 的类定义一般都是考虑很多方面,比如DataSet,很多属性或者不必要的字段,初始化都会暂用内存。
3.WCF消息编码的例子网上也有,我自己的测试代码属于公司的项目,所以不能外出。
不过我会给你参考意见。
你可以先试验一下第一个方式。
Frank Xu Lei--谦卑若愚,好学若饥
专注于.NET平台下分布式应用系统开发和企业应用系统集成
Focus on Distributed Applications Development and EAI based on .NET
欢迎访问老徐的中文技术博客:Welcome to My Chinese Technical Blog
欢迎访问微软WCF中文技术论坛:Welcome to Microsoft Chinese WCF Forum
欢迎访问微软WCF英文技术论坛:Welcome to Microsoft English WCF Forum- 已标记为答案 Allen Chen - MSFTModerator 2009年9月25日 3:17