I've been using WCF in practice for about a year. My least favorite part of the technology is Visual Studio's service reference. I think this for several reasons:
- The generated source files are difficult to maintain
- Creating the service reference is sometimes unstable
- The increased maintenance each time the service changes
Generated source files
Creating a service reference in Visual Studio generates a lot of files. My sample project contains a simple service with a single method. The service reference for this generates 10 new files!
I find it cumbersome to maintain these files as the service changes. Files can be added and made obsolete with what seems like trivial changes made to the service. This is a challenge keeping straight in source control.
The unstable service reference
Too many times I've updated the service reference and my code doesn't compile. After some digging, I find the service reference now contains this code:
Where did my methods and types go? I remove the reference and try again. Same result. Where do I go from here? I've never found the root cause of this behavior. There is no feedback from the reference tool to tell me something's wrong. Sometimes I can get past this by deselecting the "reuse types" option before creating the reference. Other times, I cannot. This is very frustrating when it happens.
Increased maintenance when service changes
Let's say I have a service with one method. I also have three projects in my solution which consume this service. Now i decide to add a second method. Before using this new method in any of my other projects, I must update the service reference. Three times. What if I forget to do this? Well, in this example forgetting just means the new method won't be available. But what if I change my service's method to accept a new parameter? Now I have a bigger issue. My project with the old reference will still compile without any errors. But at run time, an error will occur. I want to catch these type of issues when I build. Every interface change made to the service must be propogated n times. There must be a better way.
Avoid the service reference completely!
Putting all these factors together forced me to find another way. Something cleaner, less error-prone and easier to maintain. The pattern is to break the service into separate assemblies, each with its own purpose. If you create a new WCF Service Application using the project template, Visual Studio will provide the following:
This is already starting you down a wrong path for a couple reasons. First, it combines the service and the interface into the same application. Anything that consumes this service must recreate this interface. That's part of what adding a service reference does for you. But, that means that a change to this interface requires all those copies of it to be updated. Another misstep in this template is that it defines a type in the same file as the interface. Again, it makes it very tough to share the type if it is bundled into the service application. I should state that I don't fault MS for putting everything into the project this way. It's a template afterall, and they needed to include all the needed pieces to make it work.
Clear these muddy waters by separating all three pieces into separate assemblies.
- DTO - defines types used by the service
- Client - defines the interface and client-side proxy class
- Service - the actual serivce which references both the DTO and Client
Now the only thing in the service application is the actual implementation of the service. The interface and types are in separate projects which can now be shared among our service and consumer applications. Our solution looks like this:
In addition to defining the interface in the PS.Client project, I also create a proxy class to be reused by all consumer applications. This is part of what Visual Studio does for you when you add a service reference. If you look a little deeper into what is generated, you will see it is fairly straightforward. In my example, the AccountService service contains one method, which returns an Account object. The proxy class is very simple:
Now that everything is separated, I am promoting code reuse - the service and consumer applications share both the types and the interface. All consumer applications share the same proxy class. Earlier I mentioned how using a service reference could lead to run time errors if the service interface changes. In the new setup, this is not possible. If a new parameter is added to the GetAccount service method, all consumer applications will fail to compile until they are changed. I am also not relying on a Visual Studio utility to get my service and consumers talking. If I want to add my service in a new consumer application, all I need to do is add a "normal" reference to the PS.Client & PS.DTO projects and add an endpoint to my config file. While there is a little more set up early on in the process, I've found this pattern to be beneficial in the long run. It has made the services very consistent from a structure standpoint, and it has been a lot easier to maintain as changes are made.
Download the source code:
WCFReference.zip (242.38 kb)