Hello
http://wcfproxygenerator.codeplex.com/releases/view/28579
The sample has an Abstract class ExceptionHandlingProxyBase<T> which
does implement logic to close and regenerate WCF proxies when an
exception occurs.
[ServiceContract]
public interface IFoo
{
[OperationContract]
void Bar();
int BarWithReturn();
void BarWithParam(int yeah);
}
How can I use dynamic proxy to generate a class which looks like this:
public class Proxy : ExceptionHandlingProxyBase<IFoo>, IFoo
{
public void Bar() {
base.Invoke("Bar");
}
public int Bar() {
return base.Invoke("Bar");
}
public void BarWithParam(int yeah) {
base.Invoke("BarWithParam", yeah);
}
}
Here the ExceptionHandlingProxyBase
public class ExceptionHandlingProxyBase<T> : ICommunicationObject,
IDisposable
where T : class
{
// state
private bool IsOpened { get; set; }
public bool IsDisposed { get; private set; }
// lock
private object m_channelLock = new object();
private bool m_isInitialized = false;
private bool m_isProxyCreated = false;
private ManualResetEvent m_proxyRecreationLock = new
ManualResetEvent(true);
protected int m_proxyRecreationLockWait = 1000;
// channel
private ChannelFactory<T> m_channelFactory = null;
private T m_channel = default(T);
#region Constructors
public ExceptionHandlingProxyBase()
{
}
public ExceptionHandlingProxyBase(string
endpointConfigurationName)
{
Initialize(endpointConfigurationName);
}
protected virtual void Initialize(string
endpointConfigurationName)
{
if (this.m_isInitialized) throw new
InvalidOperationException("Object already initialized.");
this.m_isInitialized = true;
m_channelFactory = new
ChannelFactory<T>(endpointConfigurationName);
}
public ExceptionHandlingProxyBase(string
endpointConfigurationName, string remoteAddress)
{
Initialize(endpointConfigurationName, remoteAddress);
}
protected virtual void Initialize(string
endpointConfigurationName, string remoteAddress)
{
if (this.m_isInitialized) throw new
InvalidOperationException("Object already initialized.");
this.m_isInitialized = true;
m_channelFactory = new
ChannelFactory<T>(endpointConfigurationName, new
EndpointAddress(remoteAddress));
}
public ExceptionHandlingProxyBase(Binding binding,
EndpointAddress remoteAddress)
{
Initialize(binding, remoteAddress);
}
protected virtual void Initialize(Binding binding,
EndpointAddress remoteAddress)
{
if (this.m_isInitialized) throw new
InvalidOperationException("Object already initialized.");
this.m_isInitialized = true;
m_channelFactory = new ChannelFactory<T>(binding,
remoteAddress);
}
#endregion
#region Proxy creation
public event EventHandler AfterRecreateProxy;
protected virtual void CreateProxy()
{
lock (this.m_channelLock)
{
if (this.m_isProxyCreated) throw new
InvalidOperationException("Proxy already created.");
CreateInnerChannel();
this.m_isProxyCreated = true;
}
}
protected virtual void RecreateProxy()
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
CreateInnerChannel();
if (AfterRecreateProxy != null)
AfterRecreateProxy(this, null);
}
}
private void CreateInnerChannel()
{
lock (this.m_channelLock)
{
if (m_channelFactory == null)
throw new InvalidOperationException("Proxy
invalid. This occurs when you use the default constructor.");
m_channel = m_channelFactory.CreateChannel();
ICommunicationObject co = m_channel as
ICommunicationObject;
co.Faulted += InnerChannel_Faulted;
co.Closed += InnerChannel_Closed;
co.Closing += InnerChannel_Closing;
co.Opened += InnerChannel_Opened;
co.Opening += InnerChannel_Opening;
}
}
#endregion
#region Communication events
private void InnerChannel_Opening(object sender, EventArgs e)
{
lock (m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
if (this.Opening != null)
this.Opening(sender, e);
}
}
private void InnerChannel_Opened(object sender, EventArgs e)
{
lock (m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
if (this.Opened != null)
this.Opened(sender, e);
}
}
void InnerChannel_Closing(object sender, EventArgs e)
{
lock (m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
if (this.Closing != null)
this.Closing(sender, e);
}
}
private void InnerChannel_Closed(object sender, EventArgs e)
{
lock (m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
try
{
this.m_proxyRecreationLock.Reset(); // will stop
other threads from trying to Invoke() while recreating the proxy
if (this.Closed != null)
this.Closed(sender, e);
OnClosed();
}
finally
{
this.m_proxyRecreationLock.Set(); // will stop
other threads from trying to Invoke() while recreating the proxy
}
}
}
protected virtual void OnClosed()
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
this.Abort();
RecreateProxy();
}
}
private void InnerChannel_Faulted(object sender, EventArgs e)
{
lock (m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
try
{
this.m_proxyRecreationLock.Reset(); // will stop
other threads from trying to Invoke() while recreating the proxy
if (this.Faulted != null)
this.Faulted(sender, e);
OnFaulted();
}
finally
{
this.m_proxyRecreationLock.Set(); // will stop
other threads from trying to Invoke() while recreating the proxy
}
}
}
protected virtual void OnFaulted()
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
this.Abort();
RecreateProxy();
}
}
#endregion
# region Channel Properties
public IClientChannel InnerChannel
{
get
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return (IClientChannel)m_channel;
}
}
public ClientCredentials ClientCredentials
{
get
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return m_channelFactory.Credentials;
}
}
public ServiceEndpoint Endpoint
{
get
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return m_channelFactory.Endpoint;
}
}
public CommunicationState State
{
get
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
IChannel channel = (IChannel)m_channel;
if (channel == null)
return CommunicationState.Created;
return channel.State;
}
}
#endregion
#region ICommunicationObject Members
public event EventHandler Closed;
public event EventHandler Closing;
public event EventHandler Faulted;
public event EventHandler Opened;
public event EventHandler Opening;
public void Abort()
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
ICommunicationObject co =
(ICommunicationObject)m_channel;
co.Closed -= new
EventHandler(this.InnerChannel_Closed);
co.Closing -= new
EventHandler(this.InnerChannel_Closing);
co.Faulted -= new
EventHandler(this.InnerChannel_Faulted);
co.Opened -= new
EventHandler(this.InnerChannel_Opened);
co.Opening -= new
EventHandler(this.InnerChannel_Opening);
co.Abort();
}
}
public void Open(TimeSpan timeout)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
if (!this.IsOpened)
{
EnsureProxy();
((ICommunicationObject)m_channel).Open(timeout);
this.IsOpened = true;
}
}
}
public void Open()
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
if (!this.IsOpened)
{
EnsureProxy();
((ICommunicationObject)m_channel).Open();
this.IsOpened = true;
}
}
}
public void Close(TimeSpan timeout)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
((ICommunicationObject)m_channel).Close(timeout);
}
}
public void Close()
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
((ICommunicationObject)m_channel).Close();
}
}
public IAsyncResult BeginClose(TimeSpan timeout, AsyncCallback
callback, object state)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return
((ICommunicationObject)m_channel).BeginClose(timeout, callback,
state);
}
}
public IAsyncResult BeginClose(AsyncCallback callback, object
state)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return
((ICommunicationObject)m_channel).BeginClose(callback, state);
}
}
public IAsyncResult BeginOpen(TimeSpan timeout, AsyncCallback
callback, object state)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return
((ICommunicationObject)m_channel).BeginClose(timeout, callback,
state);
}
}
public IAsyncResult BeginOpen(AsyncCallback callback, object
state)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
return
((ICommunicationObject)m_channel).BeginOpen(callback, state);
}
}
public void EndClose(IAsyncResult result)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
((ICommunicationObject)m_channel).EndClose(result);
}
}
public void EndOpen(IAsyncResult result)
{
lock (this.m_channelLock)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
((ICommunicationObject)m_channel).EndOpen(result);
}
}
#endregion
#region IDisposable Members
public void Dispose()
{
lock (m_channelLock)
{
Cleanup();
this.IsDisposed = true;
}
}
protected virtual void Cleanup()
{
try
{
ICommunicationObject co =
(ICommunicationObject)m_channel;
co.Closed -= InnerChannel_Closed;
co.Closing -= InnerChannel_Closing;
co.Faulted -= InnerChannel_Faulted;
co.Opened -= InnerChannel_Opened;
co.Opening -= InnerChannel_Opening;
co.Close();
}
catch
{
try
{
ICommunicationObject co =
(ICommunicationObject)m_channel;
co.Abort();
}
catch { }
}
try
{
m_channelFactory.Close();
}
catch
{
try
{
m_channelFactory.Abort();
}
catch { }
}
}
#endregion
#region Invoke
public delegate void RetryInvokeHandler(out Message
unreadMessage);
public event RetryInvokeHandler RetryInvoke;
protected void Invoke(string operationName, params object[]
parameters)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
this.Open();
MethodInfo methodInfo = GetMethod(operationName);
try
{
// manual reset event here, turn it on when faulted
// other threads will queue, and get a successful
Invoke() once proxy is recreated
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait); //
if this takes longer than 1 second we have bigger problems
methodInfo.Invoke(m_channel, parameters);
}
catch (TargetInvocationException targetEx) // Invoke()
always throws this type
{
CommunicationException commEx =
targetEx.InnerException as CommunicationException;
if (commEx == null)
{
throw targetEx.InnerException; // not a
communication exception, throw it
}
FaultException faultEx = commEx as FaultException;
if (faultEx != null)
{
throw targetEx.InnerException; // the service
threw a fault, throw it
}
try
{
// manual reset event here, turn it on when
faulted
// other threads will queue, and get a successful
Invoke() once proxy is recreated
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait); //
if this takes longer than 1 second we have bigger problems
// if it is a Message type it won't work, must
fire RetryInvoke() and hopefully derived class will supply the
original
// message to send again...
if (parameters.Length == 1&& parameters[0] is
Message)
{
Message unreadMessage;
RetryInvoke(out unreadMessage);
methodInfo.Invoke(m_channel, new object[]
{ unreadMessage }); // a communication exception, retry once
}
else
methodInfo.Invoke(m_channel, parameters); // a
communication exception, retry once
}
catch (TargetInvocationException targetEx2)
{
throw targetEx2.InnerException; // still failed,
throw it
}
}
}
protected TResult Invoke<TResult>(string operationName, params
object[] parameters)
{
MethodInfo methodInfo = GetMethod(operationName);
return Invoke<TResult>(methodInfo, parameters);
}
protected TResult Invoke<TResult>(MethodInfo methodInfo,
params object[] parameters)
{
if (this.IsDisposed) throw new
InvalidOperationException("Cannot use disposed object.");
this.Open();
TResult result = default(TResult);
try
{
// manual reset event here, turn it on when faulted
// other threads will queue, and get a successful
Invoke() once proxy is recreated
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait); //
if this takes longer than 1 second we have bigger problems
result = (TResult)methodInfo.Invoke(m_channel,
parameters);
}
catch (TargetInvocationException targetEx) // Invoke()
always throws this type
{
CommunicationException commEx =
targetEx.InnerException as CommunicationException;
if (commEx == null)
{
throw targetEx.InnerException; // not a
communication exception, throw it
}
FaultException faultEx = commEx as FaultException;
if (faultEx != null)
{
throw targetEx.InnerException; // the service
threw a fault, throw it
}
// a communication exception, retry once
try
{
// manual reset event here, turn it on when
faulted
// other threads will queue, and get a successful
Invoke() once proxy is recreated
this.m_proxyRecreationLock.WaitOne(this.m_proxyRecreationLockWait); //
if this takes longer than 1 second we have bigger problems
// if it is a Message type it won't work, must
fire RetryInvoke() and hopefully derived class will supply the
original
// message to send again...
if (parameters.Length == 1&& parameters[0] is
Message)
{
Message unreadMessage;
RetryInvoke(out unreadMessage);
result = (TResult)methodInfo.Invoke(m_channel,
new object[] { unreadMessage }); // communication exception, retry
once
}
else
result = (TResult)methodInfo.Invoke(m_channel,
parameters); // communication exception, retry once
}
catch (TargetInvocationException targetEx2)
{
throw targetEx2.InnerException; // still failed,
throw it
}
}
return result;
}
internal MethodInfo GetMethod(string operationName)
{
Type t = typeof(T);
HashSet<MethodInfo> methods = new HashSet<MethodInfo>();
GetMethodsRecursive(t, BindingFlags.Public |
BindingFlags.Instance, ref methods);
var result = from m in methods
where m.Name == operationName
select m;
if (result.Count() == 0)
throw new
InvalidOperationException(String.Format("Unable to invoke method {0}.
Method does not exist on contract {1}.", operationName,
t.ToString()));
if (result.Count()> 1)
throw new
InvalidOperationException(String.Format("Unable to invoke method {0}.
More than one method is defined on contract {1} by the same name.
Overloads not supported by CachedProxyBase.", operationName,
t.ToString()));
return result.First();
}
private void GetMethodsRecursive(Type t, BindingFlags flags,
ref HashSet<MethodInfo> methods)
{
MethodInfo[] children = t.GetMethods(flags);
methods.UnionWith(children);
foreach (Type contract in t.GetInterfaces())
{
GetMethodsRecursive(contract, flags, ref methods);
}
}
private void EnsureProxy()
{
lock (this.m_channelLock)
{
if (!this.m_isProxyCreated)
{
this.CreateProxy();
}
}
}
#endregion
}