AvocadoSoftware.com

Software For Hardcore Developers
Welcome to AvocadoSoftware.com Sign in | Join | Help
in Search

Derick Baileys old blog archives - go to derickbailey.com for new contents

Windows Communication Foundation: Lessons Learned and Notes From The Mine-Field

My current project employs the new Microsoft Windows Communication Foundation as the communications layer between the various parts of the system. It's my first production use of WCF and overall, I'm very happy with out it works and how easy it is to use. It is definitely a huge improvement over Remoting, in previous versions of .NET, and the ability to switch from Remoting to Web Service to Net Pipes to whatever other protocol is so simple, I don't even really care what the requirements are up front, for protocol / communication channel. If / when I need to change, it's only a few lines of code or a config file change.

During my usage of WCF, I have run into a few lessons learned and annoyances. Hopefully this post will help alleviate some of the same issues that others are having with their WCF implementation. I won't bore you with the details of getting WCF up and running - there are plenty of articles out there, to do that already.

Good Code Standards

The first thing that strikes me about WCF is that it prefers at least one good coding standard - separating your object's interface from it's implementation. To host an object as a host, you really should create a separate interface to host as the contract and then provide an object that implements the interface. This is a good design idea in general. If you are working with an SOA setup (which is what WCF really enables), then you don't want to tie your implementation of the contract to a specific object. Implementing an interface allows you to change the code that actually runs the contract without affecting the contract or any clients connecting to the contract. This is often called Design-By-Contract, and is one of the overarching principals in a Service Oriented Architecture.

Attributes for Service Contracts

Any interface that you wish to use as a WCF contract must be marked with the [ServiceContract] attribute. If you do not mark your interface as a [ServiceContract], you will not be able to use that interface as a WCF contract. If you try, you will get serialization exceptions and the host will not start. On top of the [ServiceContract] requirement for the interface definition, any method you want hosted by the contract must be specified as [OperationContract], within the interface definition. This allows the individual methods to be serialized into the contract.

The annoyance is that you have to mark your interfaces with various attributes and that it requires serialization to run the contracts. The understandable and acceptable portion of this, though, is that it forces explicit definition of what interfaces and methods you actually want to expose as the contract. You can create an interface within your own code that exposes dozens of methods for use only by your internal systems, then specify a few of them as [OperationContracts] and only expose that limited set to the world, through WCF. This level of fine grained control can become annoying when you are building interfaces for the sole purpose of WCF hosting, though.

Attributes for Data Contracts

Many SOA implementations will require at least some knowledge of simple objects. It becomes a readability issue to expose every service method with nothing more than simple type parameters. The use of simple objects as parameter collectors is common and suggested for more than 2 or 3 parameters on a method, in my opinion.

To provide a simple data object as a parameter or return type of a ServiceContract method, you have to use some specialized attributes again. In this case, your data entity's class must be marked as [DataContract]. This provides the specific serialization needs for your object to be exposed as a simple data object, via WCF.

After specifying your object as a [DataContract], each value member (property) that you want to expose as part of the contract must be marked as [DataMember]. Also note that any member you specify as a DataMember, must be public read/write. If you are specifying a property as a DataMember, be sure to provide both public get and set operations for the property.

Once again - failing to provide the appropriate attributes or not providing read/write access to the data members on a data objects may result in exceptions being thrown.

Distinctions between Data and Service Contracts

Note that there are significant differences between ServiceContract and DataContract, here. At a code writing level, a ServiceContract is declared as an interface and a DataContract is declared as a class, struct, or enum - data value objects (entities). This difference may seem insignificant, but the runtime difference that is caused by these separate attributes is quite large.

An interface marked as a ServiceContract, and an object implementing that interface, will be hosted by WCF as a "server" - the code will live and run on the machine that is hosting it. Any reference that you make to a ServiceContract object, as a client, will be a proxy of some sort - either a remoting "__transparentProxy" or a web service proxy, or whatever other channel's proxy. The code that you execute on a ServiceContract object, as a client, will actually be executed on the server. The client code will never have an actual instance of the ServiceContract object.

An object marked as DataContract, though, will have all of it's DataMember members serialized and sent across the WCF channel as a simple object. This object will then be deserialized on the client, and you will have an instance of that data in memory on the client.

Serialization

Yes, your heard it right - WCF hosted contracts (Service and Data) require serialization to work.

Why serialization? Each of these protocols has unique needs in how it describes the contract that being exposed. By enforcing a simple serializable requirement on your contract, you are no longer concerned with the need to expose the interface to the specific communication channel in a manner specific to each channel. 

Why create specialized serialization attributes, rather than use the existing [Serializable] attributes? My only guess for this question is that they wanted to provide a specific implementation, separated from the normal serialization, so that we can be explicit in our definition of which objects are serializable for WCF and which are serializable through normal serialization processes.

Singleton vs. Per-Request ServiceContract Objects

One of the features that I really like about WCF is that it's really easy to specify a single instance of an object to be hosted as the ServiceContract implementer for all requests. Simply provide the instance to the ServiceHost when you instantiate it, or if you are using config files, there is a simple attribute and value you can set to tell it to use a single instance.

What's even better, though, is that Microsoft is including some Inversion Of Control ("IoC") in WCF, allowing us to add more than just Singleton and Per-Request contract object references. A simple implementation of the IInstanceProvider interface allows you to instantiate a requested contract object in any manner that you wish. I won't go into detail in this post - you can look it up yourself. I have personally created an IInstanceProvider object that makes a call to my Dependency Injection tool, to retrieve the contract object. An excellent implementation of this can be found in Castle.Windsor [link points to the Castle SVN repository].

Code vs Configuration

As a personal preference, I find it to be easier to implement WCF via code instead of configuration. I don't like massive XML configuration files - they are bothersome and tedious to maintain. For my current project, I have created a WCFHelper object and attribute set that allows me to apply an attribute to a given object, and have it automatically loaded up as a WCF host for the specified ServiceContract. I hope to post that code someday. I'm quite proud of it, though it does introduce some limitations in the WCF implementation for a given project. I would recommend using a WCF Factory like this, within your specific projects and not try to implement a generic one that can be used in any project. You'll find that you end up re-inventing the entire WCF API set if you try to make everything in a WCF Factory, configurable.

Minor Attribute Annoyance

Microsoft decided that it wanted to ship the DataContract and DataMember attributes as part of the System.Runtime.Serialization assembly. This assembly is specifically part of .NET 3.0 and WCF.

They also decided to ship ServiceContract and OperationContract attributes as part of the System.ServiceModel assembly. This assembly, again, is specifially part of .NET 3.0 and WCF.

If all of these attributes are used for serialization, and all of them are used for WCF specifically, why provide them in two separate assemblies? I spent probably 30 minutes trying to figure out why my objects couldn't be marked as DataContracts, when I first started working with WCF. It took a lot of Googling to figure out that I needed to add a reference to System.Runtime.Serialization.

I don't know why it was done this way... it confuses me, baffles me, frustrates me, and makes me wonder about the intelligence and organization skills of some of the Microsoft developers... but I got over it pretty quickly.

Conclusion

If you have not worked with WCF yet, and you are considering a remoting or web services implementation within a .NET 2.0 project - I would highly recommend looking at WCF as your implementation method. This will require use of the .NET 3.0 extensions, of course, but the cost is worth the benefit.

I honestly believe that Microsoft got this one right. WCF has been a huge improvement in productivity for myself when compared to remoting and web services in .NET 1.x/2.0.

Published Wednesday, July 25, 2007 1:36 PM by dredge
Filed Under: , ,
New Comments to this post are disabled

This Blog

Post Calendar

<July 2007>
SuMoTuWeThFrSa
24252627282930
1234567
891011121314
15161718192021
22232425262728
2930311234

Advertisement

News

this is my old blog archives - go to http://derickbailey.com for updates

Syndication

Advertisement

Powered by Community Server, by Telligent Systems