简体   繁体   中英

WCF CommunicationException when transmitting larger result sets

When accessing a database via a WCF WebService the following error occurs: An error (Unable to read data from the transport connection: The connection was closed.) occurred while transmitting data over the HTTP channel.

I am using the following test case to reproduce the error.

[TestCase]
public void TestLargeDBs()
{
    var cl = new DBUpdaterClient();
    var d1 = cl.ExecuteReaderUGV("test", "SELECT * from tblPerson LIMIT 10000");
    d1 = cl.ExecuteReaderUGV("test", "SELECT * from tblPerson");
    d1 = cl.ExecuteReaderUGV("test", "SELECT * from angebotdetails");
}

The first result contains 10k rows, the second 20k and the third 225k. The error can occur at any call but sometimes it doesn't occur at all. At first I thought it was related to the message length but now I have no real idea what the problem is...Has anyone some hints for me?

Tracing does not produce any output though the files exist and it's configured correctly I think...

Web.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true" >
        <listeners>
          <add name="xml" 
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="nochwas.svclog"/>
        </listeners>
      </source>
      <source name="System.ServiceModel.MessageLogging">
        <listeners>
          <add name="messagelistener"
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="myMessages.svclog"></add>
        </listeners>
      </source>
      <source name="myUserTraceSource"
              switchValue="Information, ActivityTracing">
        <listeners>
          <add name="xml" 
               type="System.Diagnostics.XmlWriterTraceListener"
               initializeData="myActivities.svclog"/>
        </listeners>
      </source>
    </sources>
    <trace autoflush="true" />
    <sharedListeners>
      <add name="xml"
           type="System.Diagnostics.XmlWriterTraceListener"
           initializeData="Error.svclog" />
    </sharedListeners>
  </system.diagnostics>
  <appSettings>
    <add key="aspnet:UseTaskFriendlySynchronizationContext" value="true" />
  </appSettings>
  <system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" maxRequestLength="2147483647" executionTimeout="60"/>
  </system.web>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logEntireMessage="true"
                      logMessagesAtServiceLevel="false"
                      logMessagesAtTransportLevel="false"
                      logMalformedMessages="true"
                      maxMessagesToLog="5000"
                      maxSizeOfMessageToLog="2000">
      </messageLogging>
    </diagnostics>
    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- Legen Sie die Werte unten vor der Bereitstellung auf "false" fest, um die Veröffentlichung von Metadateninformationen zu vermeiden. -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- Damit in Fehlern Ausnahmedetails zum Debuggen angezeigt werden, legen Sie den Wert unten auf "true" fest. Legen Sie ihn vor der Bereitstellung auf "false" fest, um die Veröffentlichung von Ausnahmeinformationen zu vermeiden. -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <!-- Umgeht eine CommunicationException, die auftritt, wenn der ObjectGraph überläuft -->
          <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IDBUpdater"
                 closeTimeout="00:10:00"
                 openTimeout="00:10:00"
                 receiveTimeout="00:10:00"
                 sendTimeout="00:10:00"
                 maxBufferSize="2147483647"
                 maxBufferPoolSize="2147483647"
                 maxReceivedMessageSize="2147483647"
                 allowCookies="false"
                 bypassProxyOnLocal="false"
                 hostNameComparisonMode="StrongWildcard"
                 messageEncoding="Text"
                 textEncoding="utf-8"
                 transferMode="StreamedResponse"
                 useDefaultWebProxy="true">
          <readerQuotas maxDepth="2147483647"
                        maxStringContentLength="2147483647"
                        maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647"
                        maxNameTableCharCount="2147483647" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <protocolMapping>
      <remove scheme="http" />
      <add scheme="http" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IDBUpdater" />
    </protocolMapping>
    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <security>
      <requestFiltering>
        <requestLimits maxAllowedContentLength="2147483647" />
      </requestFiltering>
    </security>
    <modules runAllManagedModulesForAllRequests="true"/>
    <!--
    Um das Stammverzeichnis der Webanwendung beim Debuggen auszuwählen, legen Sie den Wert unten auf "true" fest.
    Legen Sie ihn vor der Bereitstellung auf "false" fest, um die Veröffentlichung von Informationen über den Webanwendungsordner zu vermeiden.
  -->
    <directoryBrowse enabled="true"/>
  </system.webServer>
</configuration>

app.config

<?xml version="1.0" encoding="utf-8"?>
<configuration>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_IDBUpdater"
                 closeTimeout="00:10:00"
                 openTimeout="00:10:00"
                 receiveTimeout="00:10:00"
                 sendTimeout="00:10:00"
                 maxBufferSize="2147483647"
                 maxBufferPoolSize="2147483647"
                 maxReceivedMessageSize="2147483647"
                 allowCookies="false"
                 bypassProxyOnLocal="false"
                 hostNameComparisonMode="StrongWildcard"
                 messageEncoding="Text"
                 textEncoding="utf-8"
                 transferMode="StreamedResponse"
                 useDefaultWebProxy="true">
          <readerQuotas maxDepth="2147483647"
                    maxStringContentLength="2147483647"
                    maxArrayLength="2147483647"
                    maxBytesPerRead="2147483647"
                    maxNameTableCharCount="2147483647" />
        </binding>
      </basicHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior>
          <!-- Legen Sie die Werte unten vor der Bereitstellung auf "false" fest, um die Veröffentlichung von Metadateninformationen zu vermeiden. -->
          <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
          <!-- Damit in Fehlern Ausnahmedetails zum Debuggen angezeigt werden, legen Sie den Wert unten auf "true" fest. Legen Sie ihn vor der Bereitstellung auf "false" fest, um die Veröffentlichung von Ausnahmeinformationen zu vermeiden. -->
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <!-- Umgeht eine CommunicationException, die auftritt, wenn der ObjectGraph überläuft -->
          <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <client>
      <endpoint address="http://10.1.58.48/DBUpdate/UpdateService.svc"
    binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IDBUpdater"
    contract="DBUpdater.IDBUpdater" />
    </client>
  </system.serviceModel>
  <system.data>
    <DbProviderFactories>
      <remove invariant="MySql.Data.MySqlClient" />
      <add name="MySQL Data Provider" invariant="MySql.Data.MySqlClient" description=".Net Framework Data Provider for MySQL" type="MySql.Data.MySqlClient.MySqlClientFactory, MySql.Data, Version=6.9.6.0, Culture=neutral, PublicKeyToken=c5687fc88969c44d" />
    </DbProviderFactories>
  </system.data>
</configuration>

I nearly read everything about CommunicationExceptions with WebServices that I could find but I can't get the WebService to work in a reliable way without crashing. I appreciate any suggestions from you guys!

Exception details from the client side:

An error (Unable to read data from the transport connection: The connection was closed.) occurred while transmitting data over the HTTP channel. (CommunicationException (IOException))

bei System.ServiceModel.Channels.HttpInput.WebResponseHttpInput.WebResponseInputStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   bei System.ServiceModel.Channels.MaxMessageSizeStream.Read(Byte[] buffer, Int32 offset, Int32 count)
   bei System.IO.BufferedStream.Read(Byte[] array, Int32 offset, Int32 count)
   bei System.Xml.EncodingStreamWrapper.Read(Byte[] buffer, Int32 offset, Int32 count)
   bei System.Xml.XmlBufferReader.TryEnsureBytes(Int32 count)
   bei System.Xml.XmlBufferReader.GetBuffer(Int32 count, Int32&amp; offset, Int32&amp; offsetMax)
   bei System.Xml.XmlUTF8TextReader.ReadText(Boolean hasLeadingByteOf0xEF)
   bei System.Xml.XmlUTF8TextReader.Read()
   bei System.Runtime.Serialization.XmlSerializableReader.Read()
   bei System.Data.DataTextReader.Read()
   bei System.Data.XmlDataLoader.LoadColumn(DataColumn column, Object[] foundColumns)
   bei System.Data.XmlDataLoader.LoadTable(DataTable table, Boolean isNested)
   bei System.Data.XmlDataLoader.LoadData(XmlReader reader)
   bei System.Data.DataTable.ReadXmlDiffgram(XmlReader reader)
   bei System.Data.DataTable.ReadXml(XmlReader reader, XmlReadMode mode, Boolean denyResolving)
   bei System.Data.DataTable.ReadXmlSerializable(XmlReader reader)
   bei System.Data.DataTable.System.Xml.Serialization.IXmlSerializable.ReadXml(XmlReader reader)
   bei System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadIXmlSerializable(XmlSerializableReader xmlSerializableReader, XmlReaderDelegator xmlReader, XmlDataContract xmlDataContract, Boolean isMemberType)
   bei System.Runtime.Serialization.XmlDataContract.ReadXmlValue(XmlReaderDelegator xmlReader, XmlObjectSerializerReadContext context)
   bei System.Runtime.Serialization.XmlObjectSerializerReadContext.ReadDataContractValue(DataContract dataContract, XmlReaderDelegator reader)
   bei System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator reader, String name, String ns, Type declaredType, DataContract&amp; dataContract)
   bei System.Runtime.Serialization.XmlObjectSerializerReadContext.InternalDeserialize(XmlReaderDelegator xmlReader, Type declaredType, DataContract dataContract, String name, String ns)
...

There are several entries with different durations...sometimes the exception occurs after 1 second, sometimes after 9, but it is always the same exception.

If it works sometimes and sometimes not you really have to check wcf logs at the server and client site to determine what goes wrong. In this case I suspect the client. You already have trace logging for the server side, try adding it to the client site and check the errors in the client and server logs:

Enable tracing (be sure the dir c:\\log exits and you have the right access):

<configuration>
<system.diagnostics>
  <sources>
        <source name="System.ServiceModel" 
                switchValue="Information, ActivityTracing"
                propagateActivity="true">
        <listeners>
           <add name="traceListener" 
               type="System.Diagnostics.XmlWriterTraceListener" 
               initializeData= "c:\log\Traces.svclog" />
        </listeners>
     </source>
  </sources>
</system.diagnostics>
</configuration>

Tracing WCF

After consulting an expert in WebServices he gave me the keyword MTOM (Message Transmission Optimization Mechanism) . In general WebServices are not designed to transfer big datasets but with the MTOM enhancement this is possible.

My new web.config:

<behaviors>
  <serviceBehaviors>
    <behavior name="svcBehavior">
      <serviceMetadata httpGetEnabled="true" httpsGetEnabled="true"/>
      <serviceDebug includeExceptionDetailInFaults="true"/>
      <dataContractSerializer maxItemsInObjectGraph="2147483646"/>
    </behavior>
  </serviceBehaviors>
</behaviors>

<bindings>
  <wsHttpBinding>
    <binding name="MTOMBinding"
             messageEncoding="Mtom"
             closeTimeout="00:10:00"
             openTimeout="00:10:00"
             sendTimeout="00:10:00"
             maxBufferPoolSize="2147483647"
             maxReceivedMessageSize="2147483647">
      <readerQuotas maxDepth="2147483647"
                    maxStringContentLength="2147483647"
                    maxArrayLength="2147483647"
                    maxBytesPerRead="2147483647"
                    maxNameTableCharCount="2147483647" />
    </binding>
  </wsHttpBinding>
</bindings>

<services>
  <service behaviorConfiguration="svcBehavior" name="DBUpdateService.UpdateService">
    <endpoint address="" binding="wsHttpBinding" bindingConfiguration="MTOMBinding" contract="DBUpdateService.IDBUpdater" />
  </service>
</services>

app.config (client side)

<system.serviceModel>
  <bindings>
    <wsHttpBinding>
      <binding name="MTOMBinding" closeTimeout="00:01:00" openTimeout="00:01:00"
          receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
          transactionFlow="false" hostNameComparisonMode="StrongWildcard"
          maxBufferPoolSize="524288" maxReceivedMessageSize="2147483647"
          messageEncoding="Mtom" textEncoding="utf-8" useDefaultWebProxy="true"
          allowCookies="false">
        <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="2147483647"
            maxBytesPerRead="4096" maxNameTableCharCount="16384" />
      </binding>
    </wsHttpBinding>
  </bindings>
    <client>
        <endpoint address="http://xxx/UpdateService.svc"
                  binding="wsHttpBinding" 
                  bindingConfiguration="MTOMBinding"
                  contract="UpdateService.IDBUpdater" />
    </client>
</system.serviceModel>

I am using wsHttpBinding but I am sure MTOM can be used with other bindings, too. I completely removed all other bindings because client and server had trouble exchanging data when basicHttpBinding was also configured in web.config. But I am sure that's not necessary if the configuration is correctly done.

I ran some tests with datatables that contain over 200k rows that were transmitted several times by the Web Service and there was no CommunicationException! So if you have trouble with your Web Service and large datasets try MTOM!

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM