In one of the projects we are using WCF as a gateway that routes or aggregates messages to or from other services. We are trying to keep the business logic implementation away from this layer and move it to the backend and use it more as a way to provide a canonical interface for other clients.
We stumbled upon a situation that we required to pass the exceptions from one service to other services. The first solution was:
- Create a generic fault contract in the WCF middleware,
- Capture exceptions from other services in the WCF middleware,
- Create a new instance of the generic fault contract,
- Map the exception data from the source fault contract to the generic fault contract,
- Throw the generic fault contract
The problem with this approach was there is a performance overhead for creating a new exception, throwing, and catch it in multiple layers. Moreover, handling the mapping was another issue.
Each service throws its own type of fault exception, but they receive and understand only generic fault contract defined in the WCF middleware.
The second, and presumably better approach, is to define a common type of exceptions and reuse them in different services. In this case the fault contracts are shared between all services. Take this scenario as an example:
- Service A throws an exception of type ServiceA.BusinessException,
- WCF middleware pass the same exception of type WCFMiddleware.BusinessException to the Service B that acts as a client
- Service B catches the exception of type WCFMiddleware.BusinessException and acts upon that
Now, our approach is to replace all the highlighted namespaces in the above three steps with a common type namespace i.e. CommonType.BusinessException.
In the WCF middleware, create a new project called CommonTypes and move all your fault exception contracts to that project.
In the WCF Middleware project, add a reference to CommonType project, and decorate the operations with the FaultContract attribute of type BusinessException.The implementation of Echo service, calling the Service A Echo operation and return the results:
In order to share exceptions with other platforms, there is a good blog post here, I summarised that here for our project:
- Build the project,
- Using the svcutil.exe, generate the schema for the CommonTypes.dll assembly. This command generate the XSD file for the data contracts available in the assembly. For each XML namespace it generates an XSD file.
svcutil.exe /dconly CommonTypes\bin\CommonTypes.dll
- Because we are using the WCF and .net in general, it is easier to generate a common type managed code (C#, or VB.net) of the XSD file we want to share between different WCF applications. The following command generates a C# class based on the schema.
svcutil.exe /dconly /language:CS hea.com.exceptions.xsd /out:CommonTypes.cs
Reusing Fault Types as Service
Reusing types in another service is very easy. The C# class (CommonTypes.cs) that we generated in the previous step already contains all the Data Contracts - in this case fault contracts - we want to share.
- Create a WCF service (Service A),
- Add another project (CommonTypes) and add the CommonTypes.cs to that project.
- Add a reference of CommonTypes project to the main Service project.
- Decorate operations with fault contracts imported from CommonTypes.cs (hea.com.exceptions is the namespace I gave to the BusinessException in previous step)
- Throw them in service level when necessary.
Adding Service Reference to WCF Middleware
Now that we have Both Service A and WCF middleware ready, we can consume Service A from WCF middleware. Simply right click on the WCF middleware project and add a service reference to the Service A.
Click on advanced button and check "Reuse types in reference assemblies". Specify to reuse CommonTypes in the following box. In this way, WCF client reuses any assembly that has the same namespace with the Service A from the CommonTypes assembly.
We can rewrite our WCF middleware to call Service A. Note that if you don't need to do anything with the exception, you can remove the catch block below. Since the fault contract in Service A is the same as the one in WCF middleware, it automatically throw it to the client side.
Adding Reference From WCF Middleware
This is the same as adding reference to the WCF middleware. We need to have a CommonType project and add the CommonTypes.cs that we generated from the first step to that. When adding the WCF middleware service reference to that ask WCF to reuse CommonTypes whenever the namespace matches.
In this way all three layers (Service A, WCF middleware, and Client) have the same definition of specific Fault contract. This reduces the overhead of defining different exception types, throwing, and catching blocks.