Introducing ICommunicationObject

In the Windows Communication Foundation, there are a few classes that are so fundamental that they are a supertype of almost every other class you have to deal with.  For the world of WCF channels, ICommunicationObject is that class.

 public interface ICommunicationObject : IDisposable
{
   CommunicationState State { get; }

   event EventHandler Closed;
   event EventHandler Closing;
   event EventHandler Faulted;
   event EventHandler Opened;
   event EventHandler Opening;

   void Abort();
   IAsyncResult BeginClose(AsyncCallback callback, object state);
   IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback callback, object state);
   IAsyncResult BeginOpen(AsyncCallback callback, object state);
   IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback callback, object state);
   void Close();
   void Close(TimeSpan timeout);
   void EndClose(IAsyncResult result);
   void EndOpen(IAsyncResult result);
   void Open();
   void Open(TimeSpan timeout);
}

The role of a communication object is to implement a particular state machine we use to model communication resources.  It isn't really intuitive looking at ICommunicationObject what that state machine is, how to use it, or how to implement it.  That's unfortunate.  Those of you working with channels directly will need to know how the state machine works.  At the higher levels of service and abstraction in WCF though, some of these details are hidden.

The three main states to worry about are Created, Opened, and Closed.  A communication object starts in the Created state where you can fiddle with its settings to configure the resource.  Once a communication object leaves the Created state, it's too late to be changing settings or modifying the channel stack.  At this point the object is said to be immutable.  The most common way to leave the Created state is to engage the communication resource by calling the synchronous Open() method or the asynchronous BeginOpen() method.  Long time Windows programmers will recognize this common synchronous/asynchronous pattern; everyone else will have to wait a few weeks until I have time to cover this pattern and our asynchronous helper classes.  When you're done with the resource, you can clean it up by calling the Close() method, again in both synchronous or asynchronous versions.

Common pitfall: The IDisposable implementation will typically delegate to calling the Close() method.  It's generally better if you close network resources explicitly when you're done using them.  You might create a race condition if you defer cleaning up resources until some arbitrary point in the future.

Tricky point: Although we've just covered everything but the Abort() method and Faulted state, the two are not connected.  Calling Abort() is just a more impatient way of telling the resource to close.  The Faulted state indicates that something has gone wrong with the resource and it can no longer be used.

Extra tricky point: The method overloads without a timeout do not mean that the timeout is infinite.  They instead should timeout after some reasonable default.  This is very, very important.  Getting this wrong in an implementation can break many of our Denial of Service mitigations for slow or resource hogging clients.

Life gets better if you use the corresponding base class, CommunicationObject.  This will help take care of running the state machine.  For the rest of the week, I'll be looking at the state machine and support classes in more detail to show off all of the states and transitions.

Next time: Don't Like it? Throw it Out!