Example 6-1. The FaultException<T> class
using System; using System.Runtime; using System.Runtime.Serialization; using System.Security; using System.ServiceModel.Channels; namespace System.ServiceModel { [Serializable] //More attributes public class FaultException : CommunicationException { public FaultException( ); public FaultException(string reason); public FaultException(FaultReason reason); public virtual MessageFault CreateMessageFault( ); //More members } [Serializable] public class FaultException<T> : FaultException { public FaultException(T detail); public FaultException(T detail,string reason); public FaultException(T detail,FaultReason reason); //More members } }
Example 6-2. Throwing a FaultException<T>
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCFServiceProgramming.Library { [ServiceContract] interface ICalculator { [OperationContract] double Divide(double number1, double number2); //More methods } class Calculator : ICalculator { public double Divide(double number1, double number2) { if (number2 == 0) { DivideByZeroException exception = new DivideByZeroException(); throw new FaultException<DivideByZeroException>(exception); } return number1 / number2; } //Rest of the implementation } }
Example 6-3. Defining a fault contract
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; namespace WCFServiceProgramming.Library { [ServiceContract] interface ICalculator { [OperationContract] double Add(double number1, double number2); [OperationContract] [FaultContract(typeof(DivideByZeroException))] double Divide(double number1, double number2); //More methods } }
Example 6-4. Including the service exception in the fault message
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MethodWithError(); } class MyService : IMyContract { public void MethodWithError() { InvalidOperationException exception = new InvalidOperationException("Some error"); ExceptionDetail detail = new ExceptionDetail(exception); throw new FaultException<ExceptionDetail>(detail, exception.Message); } } }
Example 6-5. Processing the included exception
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Channels; using System.ServiceModel; using System.Runtime.Serialization; using WCFServiceProgramming.Library; using System.Diagnostics; namespace WCFServiceProgramming.Client { class MyContractClient : ClientBase<IMyContract>, IMyContract { public void MethodWithError() { Channel.MethodWithError(); } } class Program { static void Main(string[] args) { MyContractClient proxy = new MyContractClient(); try { proxy.MethodWithError(); } catch (FaultException<ExceptionDetail> exception) { Debug.Assert(exception.Detail.Type == typeof(InvalidOperationException).ToString()); Debug.Assert(exception.Message == "Some error"); } } } }
Example 6-6. SettingIncludeExceptionDetailInFaults to true in debug only
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract] public interface IMyContract { [OperationContract] void MethodWithError(); } public static class DebugHelper { public const bool IncludeExceptionDetailInFaults = #if DEBUG true; #else false; #endif } [ServiceBehavior(IncludeExceptionDetailInFaults = DebugHelper.IncludeExceptionDetailInFaults)] class MyService : IMyContract { public void MethodWithError() { InvalidOperationException exception = new InvalidOperationException("Some error"); ExceptionDetail detail = new ExceptionDetail(exception); throw new FaultException<ExceptionDetail>(detail, exception.Message); } } }
Example 6-7. ServiceHost<T> and returning unknown exceptions
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Description; using System.Diagnostics; using System.ServiceModel.Channels; namespace WCFServiceProgramming.Host { public class ServiceHost<T> : ServiceHost { public bool IncludeExceptionDetailInFaults { set { if (State == CommunicationState.Opened) { throw new InvalidOperationException("Host is already opened"); } ServiceBehaviorAttribute debuggingBehavior = Description.Behaviors.Find<ServiceBehaviorAttribute>(); debuggingBehavior.IncludeExceptionDetailInFaults = value; } get { ServiceBehaviorAttribute debuggingBehavior = Description.Behaviors.Find<ServiceBehaviorAttribute>(); return debuggingBehavior.IncludeExceptionDetailInFaults; } } } }
Example 6-8. Administratively including exceptions in the fault message
<?xml version="1.0" encoding="utf-8" ?> <configuration> <system.serviceModel> <services> <service name="MyService" behaviorConfiguration="Debugging"> </service> </services> <behaviors> <serviceBehaviors> <behavior name="Debugging"> <serviceDebug includeExceptionDetailInFaults="true"/> </behavior> </serviceBehaviors> </behaviors> </system.serviceModel> </configuration>
Example 6-9. Callback contract with fault contract
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(CallbackContract = typeof(IMyContractCallback))] public interface IMyContract { [OperationContract] void DoSomething(); } interface IMyContractCallback { [OperationContract] [FaultContract(typeof(InvalidOperationException))] void OnCallback(); } }
Example 6-10. Fault handling in out-of-band invocation
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; namespace WCFServiceProgramming.Library { [ServiceContract(CallbackContract = typeof(IMyContractCallback))] public interface IMyContract { [OperationContract] void DoSomething(); } interface IMyContractCallback { [OperationContract] [FaultContract(typeof(InvalidOperationException))] void OnCallback(); } [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] class MyService : IMyContract { static List<IMyContractCallback> m_Callbacks = new List<IMyContractCallback>(); public void DoSomething() { IMyContractCallback callback = OperationContext.Current.GetCallbackChannel<IMyContractCallback>(); if (m_Callbacks.Contains(callback) == false) { m_Callbacks.Add(callback); } } public static void CallClients() { Action<IMyContractCallback> invoke = delegate(IMyContractCallback callback) { try { callback.OnCallback(); } catch (FaultException<InvalidOperationException> exception) { } catch (FaultException exception) { } catch (CommunicationException exception) { } }; m_Callbacks.ForEach(invoke); } } }
Example 6-11. Creating an alternative fault
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; using System.ServiceModel; namespace WCFServiceProgramming { class MyErrorHandler : IErrorHandler { #region IErrorHandler Members public bool HandleError(Exception error) { //... return true; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { FaultException<int> faultException = new FaultException<int>(3); MessageFault messageFault = faultException.CreateMessageFault(); fault = Message.CreateMessage(version, messageFault, faultException.Action); } #endregion } }
Example 6-12. Exception promotion
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; using System.ServiceModel; namespace WCFServiceProgramming { class MyErrorHandler : IErrorHandler { #region IErrorHandler Members public bool HandleError(Exception error) { //... return true; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { FaultException<InvalidOperationException> faultException = new FaultException<InvalidOperationException>(new InvalidOperationException(error.Message)); MessageFault messageFault = faultException.CreateMessageFault(); fault = Message.CreateMessage(version, messageFault, faultException.Action); } #endregion } }
Example 6-13. Logging the error log to a logbook service
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; using System.ServiceModel; using WCFServiceProgramming.Library; namespace WCFServiceProgramming { class MyContractClient : ClientBase<IMyContract>, IMyContract { public void DoSomething() { Channel.DoSomething(); } } class MyErrorHandler : IErrorHandler { #region IErrorHandler Members public bool HandleError(Exception error) { try { MyContractClient proxy = new MyContractClient(); proxy.DoSomething(); proxy.Close(); } catch { } return false; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { } #endregion } }
Example 6-14. Adding an error extension object
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; using System.ServiceModel.Dispatcher; using System.ServiceModel.Description; using System.Collections.ObjectModel; namespace WCFServiceProgramming.Library { class MyErrorHandler : IErrorHandler { public bool HandleError(Exception error) { return false; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { } } public interface IMyContract { [OperationContract] void DoSomething(); } class MyService : IMyContract, IServiceBehavior { #region IMyContract Members public void DoSomething() { } #endregion #region IServiceBehavior Members public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host) { IErrorHandler handler = new MyErrorHandler(); foreach (ChannelDispatcher dispatcher in host.ChannelDispatchers) { dispatcher.ErrorHandlers.Add(handler); } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } #endregion } }
Example 6-15. Supporting IErrorHandler by the service class
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Channels; using System.Diagnostics; using System.Runtime.Serialization; using System.ServiceModel.Dispatcher; using System.ServiceModel.Description; using System.Collections.ObjectModel; namespace WCFServiceProgramming.Library { class MyErrorHandler : IErrorHandler { public bool HandleError(Exception error) { return false; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { } } public interface IMyContract { [OperationContract] void DoSomething(); } class MyService : IMyContract, IServiceBehavior, IErrorHandler { #region IMyContract Members public void DoSomething() { } #endregion #region IServiceBehavior Members public void AddBindingParameters(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase host) { foreach (ChannelDispatcher dispatcher in host.ChannelDispatchers) { dispatcher.ErrorHandlers.Add(this); } } public void Validate(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase) { } #endregion #region IErrorHandler Members public bool HandleError(Exception error) { return false; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { } #endregion } }
Example 6-16. The ErrorHandlerBehavior attribute
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel; using System.Collections.ObjectModel; using System.ServiceModel.Channels; namespace WCFServiceProgramming.Library { public class ErrorHandlerBehaviorAttribute : Attribute, IServiceBehavior, IErrorHandler { protected Type ServiceType { get; set; } #region IServiceBehavior Members public void AddBindingParameters(ServiceDescription description, ServiceHostBase host, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase host) { ServiceType = description.ServiceType; foreach (ChannelDispatcher dispatcher in host.ChannelDispatchers) { dispatcher.ErrorHandlers.Add(this); } } public void Validate(ServiceDescription description, ServiceHostBase host) { } #endregion #region IErrorHandler Members public bool HandleError(Exception error) { return false; } public void ProvideFault(Exception error, System.ServiceModel.Channels.MessageVersion version, ref System.ServiceModel.Channels.Message fault) { } #endregion } }
Example 6-17. Implementing AddErrorHandler( )
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel; using System.ServiceModel.Description; using System.Diagnostics; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; using System.Collections.ObjectModel; using WCFServiceProgramming.Library; namespace WCFServiceProgramming.Host { public class ServiceHost<T> : ServiceHost { class ErrorHandlerBehavior : IServiceBehavior, IErrorHandler { IErrorHandler m_ErrorHandler; public ErrorHandlerBehavior(IErrorHandler errorHandler) { m_ErrorHandler = errorHandler; } #region IErrorHandler Members public bool HandleError(Exception error) { return false; } public void ProvideFault(Exception error, MessageVersion version, ref Message fault) { } #endregion #region IServiceBehavior Members public void AddBindingParameters(ServiceDescription description, ServiceHostBase host, Collection<ServiceEndpoint> endpoints, BindingParameterCollection bindingParameters) { } public void ApplyDispatchBehavior(ServiceDescription description, ServiceHostBase host) { foreach (ChannelDispatcher dispatcher in host.ChannelDispatchers) { dispatcher.ErrorHandlers.Add(this); } } public void Validate(ServiceDescription description, ServiceHostBase host) { } #endregion } List<IServiceBehavior> m_ErrorHandlers = new List<IServiceBehavior>(); public void AddErrorHandler(IErrorHandler errorHandler) { if (State == CommunicationState.Opened) { throw new InvalidOperationException("Host is already opened"); } IServiceBehavior errorHandlerBehavior = new ErrorHandlerBehavior(errorHandler); m_ErrorHandlers.Add(errorHandlerBehavior); } public void AddErrorHandler() { if (State == CommunicationState.Opened) { throw new InvalidOperationException("Host is already opened"); } IServiceBehavior errorHandlerBehavior = new ErrorHandlerBehaviorAttribute(); m_ErrorHandlers.Add(errorHandlerBehavior); } protected override void OnOpening() { foreach (IServiceBehavior behavior in m_ErrorHandlers) { Description.Behaviors.Add(behavior); } base.OnOpening(); } //Rest of the implementation } }
Example 6-18. Implementing IEndpointBehavior
using System; using System.Collections.Generic; using System.Linq; using System.Text; using WCFServiceProgramming.Library; using System.ServiceModel; using System.ServiceModel.Description; using System.ServiceModel.Dispatcher; using System.ServiceModel.Channels; namespace WCFServiceProgramming { class MyClient : IMyContractCallback, IEndpointBehavior { #region IMyContractCallback Members public void OnCallback() { } #endregion #region IEndpointBehavior Members public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime behavior) { IErrorHandler handler = new MyErrorHandler(); behavior.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(handler); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher dispatcher) { } public void Validate(ServiceEndpoint endpoint) { } #endregion } }
Example 6-19. Implementing CallbackErrorHandlerBehavior attribute
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.ServiceModel.Description; using System.ServiceModel.Channels; using System.ServiceModel.Dispatcher; namespace WCFServiceProgramming.Library { public class CallbackErrorHandlerBehaviorAttribute : ErrorHandlerBehaviorAttribute, IEndpointBehavior { public CallbackErrorHandlerBehaviorAttribute(Type clientType) { ServiceType = clientType; } #region IEndpointBehavior Members public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection parameters) { } public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime behavior) { behavior.CallbackDispatchRuntime.ChannelDispatcher.ErrorHandlers.Add(this); } public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher dispatcher) { } public void Validate(ServiceEndpoint endpoint) { } #endregion } }