There are multiple ways of sharing WCF contract types across service boundaries. The recommended way, which I have no intent of undermining, is sharing contracts via service metadata. This means that the contract CLR types can be private (internal) to the service, and regenerated on the consumer’s side from the service metadata.
This provides full decoupling between the service and its consumers, guarantees that there is no dependency on CLR-specific information that can’t be conveyed in an interoperable fashion, and ensures that only contractual information is shared. For example, a method declared on a data contract type is not shared; a custom non-WCF attribute on a service operation contract is not shared, etc.
The primary disadvantage of this approach in a practical development scenario is that it makes deploying and testing services quite complicated, especially if the services are undergoing constant change. Additionally, it often introduces difficult-to-resolve scenarios, such as those when two services have a dependency on each other or when a service has a dependency on an infrastructure (non-service) component which in turn consumes the service when used for the implementation of other services.
The alternative to sharing contracts via metadata is sharing contracts via their assembly. This means that the contract CLR types are contained in a shared assembly that is referenced by the service and its consumers. No generation of contracts from metadata is necessary, but only .NET consumers (aware of WCF) can consume such services. Additionally, strong coupling is introduced as methods and additional non-contractual implementation leaks across service boundaries.
On the other hand, this approach is very easy to use while developing services. You change the contract in a single place and the change is automatically observed by the service implementation and by its consumers. If you’re part of a team writing multiple services which have to communicate with one another, this is the most time-conserving solution available.
In spite of the above, there is a third solution that I’m using with relative success in my current project (co-invented by Alon Fliess and myself). This is a pragmatic technique that facilitates sharing the contract code but still allowing for a redefinition or addition of data and behavior on each side of the service boundary.
What we have come up with is a fairly simple approach. Each service contract and each individual data contract (including fault contracts) is defined in a separate code file in the service’s project (assembly). All data contracts are defined as partial classes. All contracts are defined as internal.
Service consumers add a link to the code files they find relevant, and may choose to replace a link to a code file with a customized replica of it, or extend one of the data contracts by extending the partial class.
We called this technique WCF contract links. This trick relies on Visual Studio’s ability to have a link to a code file, and features the flexibility and convenience of both approaches:
- A contract can always be customized on either side of the service boundary.
- No special work is required to generate contracts on the consumer side – add a link to a code file and you’re good to go.
- You can have a contract link to an assembly and reference that assembly because the contracts are internal.
There are some obvious caveats and limitations with this approach, but with enough discipline they can be overcome:
- It is now possible to leak implementation details via the shared code file. This is something to look for in code reviews or using static code analysis rules.
- If you need to use contracts in high-level application code, their being internal can compromise some constructs. This can be addressed by making contracts public on a case-by-case basis (this creates the risk of referencing an assembly that you have a contract link to, which creates a compilation ambiguity).
- This approach is only applicable for WCF consumers, preferably within the same team for access to the linked code file. This is not the way to go for sharing services across team or enterprise boundaries.