WCF - Error Handling
Regardless of what type of application we are creating, mistakes can always happen. The same goes for services and are not exempt from that. In the case of services, the errors can be as varied as possible, with problems in transport (protocol), delivery / receipt of the message at runtime or even (and most common) in the execution of the operation (method ). WCF provides several techniques to analyze and treat the possible errors that occur during the execution of the service. The challenge here is how to make this problem (error) is passed to the client that consumes regardless of platform, giving it the ability to know what happened and how to bypass it, keeping the client application and proxy stable. This article will address how we should proceed to throw errors, notify the customer and how it can do to handle errors that occur. .NET Framework is an error with a class that inherits directly or indirectly from the class Exception . Codes that are conducive to fire it (access to files, databases and services) should be involved in blocks Try / Finally to prevent these exceptions undermine the application, preventing it to be closed suddenly. This is all true when we are using the traditional way of programming, invoking components, etc., but when we are using services, it changes a little. Exceptions are features of the language itself, and interoperability issues should not be exposed in this way, since not all languages / platforms used to represent error exceptions. Fortunately, the SOAP specification also covers how to represent errors that occur during the execution of the operation. This specification states that any errors that occur should be represented by a SOAP Fault , making that information is placed in the section body of the SOAP envolope, bringing the information about the problem happened. The SOAP Faults are interoperable way of error, allowing applications to several platforms provide their own way of treating the problem. The latest version of SOAP (version 1.2) protocol, the elements that represent the SOAP Fault are: Code ( required), Reason (Required) Role(Optional), Detail (Optional) and Node (Optional). Only the binding BasicHttpBinding uses version 1.1 protocol for interoperability with ASP.NET Web Services. We will see later in this article, the ways we have to specify the value for each of these elements. WCF introduced several types of exceptions, and among them, the classCommunicationException . This class is base for all exceptions that are raised and are related to the execution of the service, whether the problem is related to runtime or operation. Among the exceptions that derive from this class, the most important is the class FaultException . This class represents a SOAP Fault that we have seen above and as was to be expected, provides properties such as Action , Code , etc. there is another exception, not the least, yet called FaultException <TDetail> . The generic version of this class, which inherits directly from classFaultException , allows you to report a type ( TDetail ) with more details about the error that occurred and, contrary to what many think, there is no constraint on the generic type that requires TDetail is a derived class of the class Exception ; You can specify any type, since it can be serialized ( SerializableAttribute orDataContractAtrribute ). The image below shows the hierarchy of these exceptions:
![]() |
Figure 1 - Hierarchy of exceptions. |
Before we delve into the use of class FaultException , we need to understand how the behavior will be the instance of the class / proxy when an exception happens. The behavior will vary according to the type of management adopted instance ( PerSession , PerCall and Single ). mode PerSession all exceptions will terminate the session, discarding the class instance that represents the service and the proxy can not make subsequent calls, can only shut it down. But when the service is exposed through the way PerCall and an exception is thrown, the class instance that represents the service will be discarded and the proxy can not do any more call, just close it. Finally, mode Single , when an exception is thrown, the service instance is not discarded and remain active, but the proxycan not make subsequent requests. Fault Contract Classes FaultException and FaultException <TDetail> are used to represent a SOAP Fault , and any problems that occur within the operation, such as FileNotFoundException, MessageQueueException , DivideByZeroException , etc., will be automatically "translated" to FaultException .Regardless of the type of exception that happen on the service side, it will come to the proxy always asFaultException and in this case, details that expose the inner workings of the service (eg Stack Trace ) will not be sent to the client. within NET Framework, if we consult the documentation of any method, we will see what parameters it accepts, what type of return and the possible exceptions he can fire. It would be very interesting if the service was also able to inform anyone who would consume it the exceptions that it can shoot for a particular operation. Fortunately this is possible thanks to Fault Contracts . The idea is to allow the client to be able to differentiate between errors generated by the execution of the method in relation to other types of errors. For that, WCF provides an attribute called FaultContractAttribute that can only be applied to methods. As the attributeDataContractAttribute , the purpose of this attribute is specified in the WSDL SOAP Faults that can be returned by the operation, spreading the right information to the client. The example below illustrates how we should proceed to apply this attribute to the service contract:
using System;
using System.IO;
using System.ServiceModel;
[ServiceContract]
public interface IArquivos
{
[OperationContract]
[FaultContract (typeof (FileNotFoundException))]
LerConteudo string (string fileName);
}
|
It is important to say that you are not limited to apply this attribute only once; if the operation launched three different types of exceptions, you can apply the attribute FaultContractAttribute for each. When this attribute is set, the client will be able to capture a more detailed exception, which is the FaultException <TDetail> . This class exposes a read-only property of type TDetail , which reflect the particular attribute type in FaultContractAttributeapplied to the contract. If any exception occurs on the operation that is not defined in some FaultContractAttribute, it will come to the client as FaultException . Observation 1: You are not restricted to specified classes derived from Exception attribute in FaultContractAttribute . Nothing prevents you from creating a custom class with detailed information about the problem and define it as the type for the attribute, however, use this technique just enabling a more readable code. Observation 2: The operations of the type one-way do not return any value and, consequently, did not return errors, which may happen when it is running. Decorating a method one way with the attribute FaultContractAttribute result in an exception being thrown when opening the host . As we have seen just above, independent of management mode instance, when any exception was fired, it would reach the client asFaultException and invalidate the use of proxy for subsequent calls, as opposed to when exceptions are "known" (those that are defined with FaultContractAttribute ) as they continue to be fired from the client side, but will not affect the operation of the proxy . The code snippet below illustrates implement the contract IArquivos we just created:
using System;
using System.IO;
using System.ServiceModel;
public class ServicoDeArquivos: IArquivos
{
public string LerConteudo (string fileName)
{
try
{
using (StreamReader sr = new StreamReader (fileName))
sr.ReadToEnd return ();
}
catch (FileNotFoundException ex)
{
throw new FaultException <FileNotFoundException> (ex);
}
}
}
|
As we can see, intercepted errors that are already waiting (as is the case FileNotFoundException ) in block catchand, within it, captured the thrown exception, instantiate the class FaultException <TDetail> specifying type as the exception that was thrown that in her builder, we can pass the instance of the thrown exception. At this time, we do nothing more than a wrapper , firing an exception that WCF can serialize the way you understand all platforms as a mistake. 's class constructor FaultException <TDetail> is overloaded, allowing inform various other information related to the problem occurred. As stated earlier, a fault has much more information in relation to a traditional exception. The main information for a FaultException are Code and Reason , where the first one refers to a code (can be customized) error; already the second property can specify one or more reasons that caused the error. To specify the properties Code and Reason , the implementation code of the service we created above will change slightly:
using System;
using System.IO;
using System.ServiceModel;
public class ServicoDeArquivos: IArquivos
{
public string LerConteudo (string fileName)
{
try
{
using (StreamReader sr = new StreamReader (fileName))
sr.ReadToEnd return ();
}
catch (FileNotFoundException ex)
{
throw new FaultException <FileNotFoundException> (
eg
new FaultReason ("The specified file was not found")
FaultCode new ("FileNotFound"));
}
}
}
|
This code allows you to enter data relevant to the error, with the chance to customize the code according to your business rule message and, moreover, can mask the real problem occurred. The picture below it is the error message that was captured by the system tracing WCF itself. Note that the information that was customized are being sent to the client.
![]() |
Figure 2 - The return message containing the customized information about the error. |
At this time, when referencing the service on the client, the proxy already contemplate the class that represents the data of the exception that, in our case, is the class FileNotFoundException . Thus, the first catch that we have in our structure error handling is just the generic version of the class FaultException specifying the class that provides the complement of the error ( FileNotFoundException ). Set over a block catch with just the classFaultException is also a good practice because it will avoid exceptions not mapped in the contract of service may jeopardize your application. The code below shows how to invoke the method within a framework of error handling:
using (ArquivosClient ArquivosClient = new proxy ())
{
try
{
Console.WriteLine (proxy.LerConteudo ("ArquivoQueNaoExiste.txt"));
}
catch (FaultException <FileNotFoundException>)
{
Console.WriteLine ("File not found");
}
catch (FaultException)
{
Console.WriteLine ("Problem in implementing the operation");
}
catch (CommunicationException)
{
Console.WriteLine ("Problem in service");
}
catch (Exception)
{
Console.WriteLine ("Error");
}
}
|
Intercepting Exceptions
Everything we've seen so far is to create an exception for her to be fired, and finally reaches the customer to be notified that something happened, and allow him make the best decision on that. But what if we want to capture any exception triggered by the service, allowing one log centrally? WCF has some extensions, which allows us to engage a certain code during the execution of the service that will ensure that it will run, giving the chance to catalogarmos the exception or even customizarmos information for her, avoiding all the time is write code to handle locally (inside the method) the exception. This section of the article has a responsibility to show how to implement this technique. The first type we need to examine is the IErrorHandler interface that is contained in thenamespace System.ServiceModel.Dispatcher . This interface has only two methods: ProvideFault and HandleError .The first, ProvideFault , runs when any exception is thrown during the execution of the transaction and before the execution back to the client. At this time, WCF provides the opportunity to transform exceptions that were not foreseen in the contract FaultException <TDetail> , ensuring that the client can receive more detailed information about the problem. Already method HandleError enables you to make log the exception that occurred. Because this method will be triggered after the execution has already returned to the client, you can make a more costly task, and you should not rely on the context of execution ( OperationContext ) because it is no longer available.This method receives as a parameter a type Exception and, theoretically, all you have to do is log the same. Notice that the method HandleError must return a Boolean value. When set to False , it will allow other error handlers can be processed. Another important detail is that if this method returns True and the management mode of the instance is set to Single , WCF does not abort the session (if it exists). The code below shows an example showing how we can proceed to implement the interface IErrorHandler :
using System;
using System.IO;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Dispatcher;
public class ErrorHandling: IErrorHandler
{
public bool HandleError (Exception error)
{
try
{
Log (error);
}
catch (Exception) {}
return false;
}
public void ProvideFault (Exception error, MessageVersion version, ref Message fault)
{
if (error is FileNotFoundException)
{
FaultException ex =
new FaultException (
(FileNotFoundException) error,
new FaultReason ("The specified file was not found")
FaultCode new ("FileNotFound"));
fault = Message.CreateMessage (version, ex.CreateMessageFault (), ex.Action);
}
}
private static void log (Exception error)
{
// To log
}
}
|
In this case, we are centralizing the promotion of exceptions that are triggered by the execution of operations within the method ProvideFault , turning them into FaultException <TDetail> . Already method HandleError only does log the exception thrown by the operation. Only the implementation itself will not work. You need to attach the instance of this class to implement the service. WCF provides an interface called IServiceBehavior which, in turn, provides a calling method ApplyDispatchBehavior . This method provides a collection of channel dispatchersthat are utilized by the host . For each dispatcher there is a property called ErrorHandlers that exposes a collection, and each element of this collection must implement the interface IErrorHandler . The code below shows how to attach the newly created class to implement the dispatcher :
public class ErrorServiceBehavior: IServiceBehavior
{
public void ApplyDispatchBehavior (ServiceDescription ServiceDescription,
ServiceHostBase ServiceHostBase)
{
foreach (ChannelDispatcher cd in serviceHostBase.ChannelDispatchers)
{
cd.ErrorHandlers.Add (new ErrorHandling ());
}
}
// Other methods
}
|
Finally, we must only accommodate this behavior the collection of behaviors of the host . Simply add the class instance ErrorServiceBehavior the collection of behaviors , exhibited by property Behaviors of ServiceHost . The example below demonstrates how to accomplish this:
host.Description.Behaviors.Add (new ErrorServiceBehavior ());
|
The property IncludeExceptionDetailInFaults
At development time, do all of these settings can be tricky because you do not get to determine which exceptions can trigger your service. For purposes of testing or even production environment, we can resort to a property called IncludeExceptionDetailInFaults that, as its name says, will send the exception details to the client, regardless of whether it was or was not included in the fault contracts . This setting can be made directly in the class representing the service, through the attribute ServiceBehaviorAttribute via ServiceDebugBehavior or even through the configuration file, adding a behavior type serviceDebug . The last option is the most flexible, since it lets you change this setting without the need to recompile the service. Conclusions: The article showed the main techniques for error handling in WCF services, as well as the behavior of each. We can use the strategies shown here to make monitoring more effective enforcement, giving a chance to the developer / administrator to catalog and view the exceptions that were fired by their service and especially the ability to notify the customer so that he can determine the What exactly happened.
Comments
Post a Comment