Question

My services simply call BusinessLogicLayer methods where entire business logic is put. I want to know what's the best practice for handling exceptions raised by BL?(not only fatal exceptions, also "logic" ApplicationExceptions like UserNotFoundException which my BL throws when can't find user).

Where should I transform these exceptions into FaultExceptions which client will see?

Should I throw my business Exceptions from BL and than catch them into service call and transform to FaultException and return to client? or BL should raise already "client friendly" FaultExceptions?

thanks in advance :)

Was it helpful?

Solution

I would say throw business exception from business logic layer, this would keep your business logic layer decoupled with wcf implementation. In service call you may override applydispatchbehaviour and add error handler there, something like

Overriding IServiceBehavior.ApplyDispatchBehavior

 void IServiceBehavior.ApplyDispatchBehavior(ServiceDescription serviceDescription, ServiceHostBase serviceHostBase)
        {
            foreach (ChannelDispatcher dispatcher in serviceHostBase.ChannelDispatchers)
            {
                dispatcher.ErrorHandlers.Add(new FaultErrorHandler());
            }
        }

FaultErrorHandler

public class FaultErrorHandler : IErrorHandler
    {      

        void IErrorHandler.ProvideFault(System.Exception error, MessageVersion version, ref Message fault)
        {
            if (fault == null)
            {
                FaultException<[ExceptionType]> fe = new
                    FaultException<[ExceptionType]>([Exception cass],
                    error.Message, FaultCode.CreateReceiverFaultCode(new FaultCode("ServerException")));
                    MessageFault mf = fe.CreateMessageFault();
                    fault = Message.CreateMessage(version, mf, fe.Action);

            }
        }

    }

OTHER TIPS

Standard .Net exceptions are correctly serialized on server side, and deserialized on client side. By default, not ours. Why ? It may be a best practise to send business exception to client during debuging sessions:
- without having to put the Exception Data in a [DataMember] object
- having more info than a simple string ( ExceptionFault<ExceptionDetail>)

But take care of not sending exceptions when putting code in production. It may cause security leaks disclosing details to hackers if your service is exposed on internet !

In order to send the business exception to the client, the best (and some mandatory) practises are :

1/ Toggle the serviceDebugBehavior on

ServiceHost host = ...;
var debuggingBehavior = host.Description.Behaviors.Find<ServiceBehaviorAttribute>();
if (debuggingBehavior == null)
  debuggingBehavior = new ServiceBehaviorAttribute();
#if DEBUG
debuggingBehavior.IncludeExceptionDetailInFaults = true;
#else
debuggingBehavior.IncludeExceptionDetailInFaults = false;
#endif

It is also pretty easy configurate it in xml

2/ On the service interface, declare some [FaultContract] :

[ServiceContract(Namespace="your namespace")]
public interface IBillingService
{
  [OperationContract]
  [FaultContract(typeof(BusinessException))]
  void RaiseBusinessException();
}

3/ A business exception should be marked as Serializable

[Serializable]
public class BusinessException : Exception
{ ... }

4/ In order to have a business exception correctly deserialized on the client side as FaultException<BusinessException>, it is important to implement a constructor taking care of deserialization. Otherwise you'll get a generic FaultException.

protected BusinessException(SerializationInfo info, StreamingContext context)
  : base(info, context)
{}

5/ If you have some extra members in you exception, serialize/deserialize them :

public DateTime CreationTime { get; set; }
protected BusinessException(SerializationInfo info, StreamingContext context)
: base(info, context)
{
  CreationTime = (DateTime)info.GetValue("CreationTime", typeof(DateTime));
}
public override void GetObjectData(SerializationInfo info, StreamingContext context)
{
  base.GetObjectData(info, context);
  info.AddValue("CreationTime", CreationTime);
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top