Share via


Some Changes for Channels and Transports in the June CTP, Part 2

One of the biggest changes, although hard to recognize as such, is that we had to change the behavior of the Close method of ICommunicationObject to fix a bug. This was a particularly difficult fix to make because changing how ICommunicationObject works has such a broad impact across the entire product. The problem was that Close automatically downgrades itself to Abort when the communication object is faulted. How is this a problem? Well, the notification of the communication object faulting comes from the Faulted event being raised. If you call Close at almost the same time as the communication object is faulting, your Close may actually be an Abort but you won't know that because the Faulted event hasn't gone out yet.

This makes it impossible to know if your call to Close successfully completed. If you have some coordinating operation, such as a transaction or durable message, then you need to know whether the Close was successful so that you can either commit or roll back as appropriate. Our solution, after looking at a lot of proposals, was to make Close throw an exception after it downgrades to Abort due to a fault. That way, you always know whether the call to Close was successful. However, this change breaks code that calls Close without handling this exception. That can happen if you call Close in the cleanup of a catch block for instance. Although Close always could have thrown an exception at this time, many people were not handling that case. The solution is to call Abort yourself rather than calling Close and relying on it to downgrade to Abort for you.

The pattern for this that we expect to be commonly used looks something like this:

 try
{
   communicationObject.SomeOperation();
   communicationObject.AnotherOperation();
    ...
   communicationObject.Close();
}
catch (...)
{
   communicationObject.Abort();
}

A smaller change to CommunicationObject that happened earlier is that we removed the base class implementation of several of the callback methods for state changes. CommunicationObject implements the state machine for you and calls into your code when an Open, Close, or Abort happens to actually do the work. The default implementation for these callback methods did nothing, but there was a lot of confusion over whether these methods should be overridden and whether the subclass should chain back to the method in the base class. Changing these methods to be abstract makes what you need to do in a subclass very clear.

Unfortunately, it's hard to achieve that same kind of clarity when making behavioral changes because we don't have validation in the compiler to tell you whether you're using an object correctly.

Next time: Some Changes for Channels and Transports in the June CTP, Part 3