Search code examples
wcffaultexception

Rethrowing strongly-typed WCF FaultException in a middle-layer service


I have a very simple 3-tier project. There is a DAL (data access layer) WCF service interacting with a data store; a BLL (business logic layer) WCF service as an intermediary between the DAL service and a client UI, and a Windows Forms client layer.

I declare a DataContract in my DAL service to act as a fault contract:

[DataContract]
public class ValidationFault
{
    [DataMember]
    public String Message { get; set; }
    [DataMember]
    public String PropertyName { get; set; }
    ...
}

My DAL service has an operation decorated with a FaultContract attribute:

[OperationContract]
[FaultContract(typeof(ValidationFault))]
Patient CreatePatient(Patient patient);

The implementation of CreatePatient throws a strongly-typed FaultException like this:

throw new FaultException<ValidationFault>(new ValidationFault("Patient last name cannot be empty.", "LastName"));

My BLL service acts as a client for the DAL service, and a service for the UI layer. In my BLL service I call the CreatePatient method of the DAL service, and if I catch the FaultException fault, I simply want to re-throw it to be handled by the client. The relevant BLL code looks like this:

...
catch (FaultException<ValidationFault>)
{
    throw;
}

I can inspect the exception in the BLL and can confirm that it is a strongly-typed FaultException and the Detail section passed from the DAL intact. The BLL method attempting to rethrow the exception is decorated with the same [FaultContract] attribute as the DAL method above.

The UI client, which is a client of the BLL service, tries to handle the exception and log/display the appropriate information. Trouble is, when this fault reaches the client, it is no longer a strongly-typed FaultException, but instead a general FaultException with a null Detail section.

I did find that if instead of just rethrowing the fault in the BLL method, I instead recreate it with the same parameters like this:

catch (FaultException<ValidationFault> valEx)
{
    throw new FaultException<ValidationFault>(new ValidationFault(valEx.Detail.Message, valEx.Detail.PropertyName));
}

then it reaches the client as a strongly-typed FaultException as expected.

My question is: why do I need to recreate this exception in the BLL service? Why can't I just pass through the strongly-typed FaultException thrown by the DAL service? I got the code to work but would like to understand what is going on. I imagine it's something that's staring me in the face but I can't for the life of me figure it out.


Solution

  • Indeed the answer was staring me in the face all along, so in case this helps someone down the road: my problem was the Action property of the FaultContract. When you decorate an operation with the FaultContract attribute, unless you specify Action explicitly, WCF automatically generates it for you. This Action includes the FaultException namespace, as well as the service and method names of the operation that generated the fault. In my case, the service and method name in my BLL layer were different than the service and method name of my DAL method that originally generated the exception, so although both my DAL and my BLL layer operations were decorated with an identical attribute:

    [FaultContract(typeof(ValidationFault))]
    

    ... the Action of the FaultContract was different. Thus when trying to rethrow the DAL FaultException in the BLL service, the FaultContract did not match the one specified on the BLL operation, and WCF was repacking the exception as a general FaultException instead of a strongly-typed one.

    My options in this case are to either re-create the FaultException in my BLL service, which creates the proper Action, but when it reaches my client, it appears to be coming from the BLL service; the fact that it originates in DAL is lost unless I specify it explicitly, or else add the Action property to the FaultContract attribute on the BLL operation, and specify the Action of the fault generated by the DAL. As to which is a better idea and better practice, that is likely a whole other topic, but I now have an understanding of what is going on, which is what I was after.