Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

18 July 2013

WCF and the Try-Catch-Abort Pattern

Proxy Classes are used to talk to Windows Communication Foundation (WCF) Services.
A communication channel is open to the WCF Service that must be closed after the calls to the service.
The proper way to close the communication channel is very important.
The WCF proxy is usually generated using Visual Studio or the svcutil tool.
The generated proxy inherits from a base class System.ServiceModel.ClientBase that implements the IDisposable interface.

The service can be called in a using block that automatically calls the Dispose() method.

using (MyService client = new MyService())
{
...
} // Dispose is called here
Dispose() calls the proxy close() method, that sends a message from the client to the service indicating that the connection session is no longer needed.
A problem can arise with this approach if there is an exception when calling the close() method. This is why the using approach is not recommended when calling WCF methods.

If the communication channel is in a faulted state Abort() should be called and not close();
The recommended approach is the Try-Catch-Abort Pattern.
This is a simple pattern where a try...catch block is used to call the service and in the catch of an exception the connection is aborted or closed.

The recomendation by Microsoft in MSDN is:
 
try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}

A generic class for creating and initializing WCF proxy clients can be created to implement the pattern and replace the using block:
 
public class WCFProxy
    {
        public static void Using<t>(Action<t> action)
        {
            ChannelFactory<t> factory = new ChannelFactory<t>("*");

            T client = factory.CreateChannel();

            try
            {
                action(client);
                ((IClientChannel)client).Close();
                factory.Close();
            }
            catch (Exception ex)
            {
                IClientChannel clientInstance = ((IClientChannel)client);
                if (clientInstance.State == System.ServiceModel.CommunicationState.Faulted)

                {
                    clientInstance.Abort();
                    factory.Abort();
                }
                else if (clientInstance.State != System.ServiceModel.CommunicationState.Closed)
                {
                    clientInstance.Close();
                    factory.Close();
                }
                throw (ex);
            }
        }
    }
To use the class is as simple as:
 
WCFProxy.Using((delegate(IMyService client)
{
  client.DoWork();
});

13 December 2011

WCF: The maximum string content length quota (8192) has been exceeded

A very annoying error I received when passing a very large string to a WCF Service was:

The formatter threw an exception while trying to deserialize the message: There was an error while trying to deserialize parameter . The InnerException message was 'There was an error deserializing the object of type System.String. The maximum string content length quota (8192) has been exceeded while reading XML data. This quota may be increased by changing the MaxStringContentLength property on the XmlDictionaryReaderQuotas object used when creating the XML reader. Line 8, position 9944.'. Please see InnerException for more details.

This is a cryptic error and only after some investigation I found the solution:

The readerQuotas element must be configured on the binding configuration:

<binding name="basicHttpBinding_IMyService" maxReceivedMessageSize="63400320">
      <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647"
       maxBytesPerRead="2147483647"   maxNameTableCharCount="2147483647"/>
</binding><br />

This is a simple example, you must configure it for your own needs.
I have added the additional configuration elements to show the other options.

01 September 2011

WCF And Debugging Exceptions

A common problem in WCF is to receive the exceptions on client when debugging.
The WCF behaviors configuration allows this using the includeExceptionDetailInFaults, but to receive the inner expection the returnUnknownExceptionsAsFaults must also be set to true.
The maxItemsInObjectGraph is only to avoid the error of exceeding the maximum number of items that can be serialized or deserialized.


<behaviors>
    <serviceBehaviors>
        <behavior name="MyServiceBehavior" returnUnknownExceptionsAsFaults="True">          
            <serviceDebug includeExceptionDetailInFaults="true" />
            <dataContractSerializer maxItemsInObjectGraph="2147483647" />
        </behavior>
    </serviceBehaviors>
</behaviors>            

15 December 2010

Fix the MaxItemsInObjectGraph quota error

When there is a great amount of data to send to a WCF service, the following error occurs:

Maximum number of items that can be serialized or deserialized in an object graph is '65536'. Change the object graph or increase the MaxItemsInObjectGraph quota.

To fix the error the MaxItemsInObjectGraph must be defined on the wcf service server and the client.

On the server:

<system.serviceModel>   
    <services>
        <service behaviorConfiguration="Service1Behavior" name="Service1">
           <endpoint address="" binding="basicHttpBinding" contract="IService1"></endpoint>
        </service>
    </services>
        <behaviors>
            <serviceBehaviors>
                <behavior name="Service1Behavior">          
                    <serviceMetadata httpGetEnabled="true"/>
                    <serviceDebug includeExceptionDetailInFaults="true"/>
                    <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
                </behavior>
            </serviceBehaviors>
        </behaviors>
    </system.serviceModel>
</configuration>

On the client:

<system.serviceModel>
    <client>
        <endpoint address="http://localhost/Service1.svc" behaviorConfiguration="Service1Behavior" binding="basicHttpBinding" contract="IService1Event" name="Service1">
      </endpoint>
    </client>
    <behaviors>
        <endpointBehaviors>
            <behavior name="Service1Behavior">
              <dataContractSerializer maxItemsInObjectGraph="2147483647"/>    
            </behavior>
        </endpointBehaviors>
</behaviors>
</system.serviceModel>

20 October 2010

Missing Edit WCF Configuration menu option in Visual Studio

Normally the context menu option Edit WCF Configuration for a app.config or web.config is not shown.
To fix this issue open up the Tools menu in Visual Studio and choose Wcf Service Configuration Editor.
The tool will open, close it straight away and then right-click in your config file.
The Edit WCF Configuration context menu is now visible.

03 September 2010

Fix error :Unable to generate a temporary class (result=1). error CS2001: Source file ‘C:\WINDOWS\TEMP\file.cs’ could not be found error CS2008: No inputs specified

When a service in ASP.Net is executed the error:

Unable to generate a temporary class (result=1). error CS2001: Source file ‘C:\WINDOWS\TEMP\filename.cs’ could not be found error CS2008: No inputs specified

can occur.

The problem are the permissions on the C:\WINDOWS\TEMP folder.
To fix it, give the user of the Application Pool full control on that folder.

16 September 2008

Linq e WCF

Exposing LINQ Generated Classes through WCF is very simple.
Just set the Serialization Mode property to Unidirectional.
The Linq designer will decorate the Linq classes with the DataContract and DataMember attributes.

04 December 2007

WCF - The remote server returned an unexpected response: (400) Bad Request.

The WCF fault "The remote server returned an unexpected response: (400) Bad Request.", might be caused by some quota or timeout on server / client side.The solution is to increase reader quota values from binding configuration on both sides:

<bindings>
<wsHttpBinding>
    <binding name="WSHttpBinding_IService" closeTimeout="00:01:00" 
    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
    maxBufferPoolSize="2000000" maxReceivedMessageSize="2000000"
    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
    allowCookies="false">
        <readerQuotas maxDepth="2000000" maxStringContentLength="2000000" maxArrayLength="2000000"
        maxBytesPerRead="2000000" maxNameTableCharCount="2000000" />
        <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>
<services>
    <service name="MyService" behaviorConfiguration="MyServiceTypeBehaviors">
        <endpoint address="" binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService" contract="MyIService"/>
        <endpoint contract="IMetadataExchange" binding="mexHttpBinding" address="mex"/>
     </service>
</services>