none
请教如何通过WCF传输大数据量数据 RRS feed

  • 问题

  • 因为有个项目需要在客户端导入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>

    请问应如何处理,谢谢。

    2009年9月18日 10:41

答案

  • 就是直接把DataSet 类型作为参数直接传递给服务端
    WCF默认支持这么做,直接传Datatable不行。

    你看一下 “服务引用设置”中你选的集合类型是什么,我选的是System.Array
    字典集合类型是默认第一项 System.Collections.Generic.Dictionary
    2009年9月21日 9:41
  • 谢谢回复

    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
    2009年9月21日 12:19
    版主

全部回复

  • 补充一下,以下是报错的完整日志信息:

    <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&amp; 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">&lt;undefined value&gt;</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>

    2009年9月18日 10:51
  • 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
    2009年9月18日 14:41
    版主
  • 非常感谢Frank Xu Lei的回复。
    之所以采用DataSet,是因为需要导入的Excel格式比较多变,如果自己定义Contract的话,我就需要针对每一个Excel模板分别定义对应的Contract,不是太灵活。
    我看了您的博客,Streaming的确在传递大数量文件上有优势,且效率更高,但在我现在的项目中可能不大适用,因为客户端是Flex。
    我现在的问题是,原方案中我传输的xml.Length=1307341,不是太大,为何会提示错误,是否还有某个地方的配置不正确?
    谢谢。
    2009年9月19日 2:44
  • 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
    2009年9月19日 6:47
    版主
  • 为什么DataSet还要转成XML传递?
    我记得可以直接传递DataSet啊。
    2009年9月21日 7:32
  • 谢谢回复

    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 云在起时:

    没试过你说的方法,有无例子?

    谢谢。
    2009年9月21日 7:51
  • 补充一点,我服务端方法接收的参数类型是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 });   
    }

    ……

    2009年9月21日 8:10
  • 就是直接把DataSet 类型作为参数直接传递给服务端
    WCF默认支持这么做,直接传Datatable不行。

    你看一下 “服务引用设置”中你选的集合类型是什么,我选的是System.Array
    字典集合类型是默认第一项 System.Collections.Generic.Dictionary
    2009年9月21日 9:41
  • 谢谢回复

    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
    2009年9月21日 12:19
    版主
  • 虽然没有找到错误的真正原因,但还是非常感谢各位的热心回复,谢谢。

    2009年9月25日 3:24