Thanks to a customer of mine (thank you Christoph!) some major improvements have been made to the ExceptionHandlingProxyBase type. Plus, a duplex version now exists. Nothing like using the code in production to work through the real limitations. I knew I had to add a lot of these things, but this week I had a good reason to do it.
The proxy generator can be found at http://wcfproxygenerator.codeplex.com
Invoke() and Value Types
**I had a problem surface when I tried to return a value type (not a reference type) from Invoke(). I created this dependency in the generic method by requiring the generic type parameter to be a class. I changed this so now you can use value types freely.
** A lock is used for all calls that touch the channel. But you can have parallel calls through Invoke() of course.
** Now you can safely have parallel calls to invoke, but if one thread encounters an exception other threads queue up before invoking the operation until the proxy is safely recreated.
** Disposal is now safe in multithreading environments.
** Now all operations check if the object is disposed and throws if it is.
** Now constructors call virtual Initialize() methods to support scenarios where the derived class wants to calculate values before calling down to base.
** Initialization only supported once.
Creation and Recreation
** Now the two are considered different so that we can enforce creation only once, while allowing multiple recreation attempts. Recreation also uses a lock to protect Invoke() and fires an event after so clients can do other work after recreation, such as subscribing to a service after the proxy is recreated.
** Clients can subscribe to these events even if channel isn't created yet. Events from the inner channel are fired to listeners.
Support for Message parameter type
** Invoke() retries didn't work if you had a contract that supports Message type. That's because you can't "replay" a Message instance, once it is read, it cannot be read again. There are two possible ways to handle this:
a) Buffer the message before each first attempt to invoke and create a copy of the message for the first and (if applicable) second call. But, this means buffering unnecessarily when things are going well.
b) allow the application to buffer in the derived type when using Message types. Then, subscribe to the RetryInvoke (new) event and supply a message before retry.
** I implemented b).
Hope you like it! Cheers!