Skip to content

Instantly share code, notes, and snippets.

@weitzhandler
Last active December 3, 2015 20:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save weitzhandler/be9e8784aac367b43d39 to your computer and use it in GitHub Desktop.
Save weitzhandler/be9e8784aac367b43d39 to your computer and use it in GitHub Desktop.
using System.ServiceModel.Channels;
using System.Threading.Tasks;
namespace System.ServiceModel
{
public class ClientProxy<TClient, TChannel> : IDisposable
where TClient : ClientBase<TChannel>
where TChannel : class
{
public TClient Client { get; private set; }
/// <summary>
/// Gets or sets the amount of trials the client should make upon failure.
/// </summary>
public int TryCount { get; set; } = 3;
public bool AutoClose { get; set; } = false;
/// <summary>
/// Will fail if no parameterless constructor.
/// </summary>
public ClientProxy()
: this(Activator.CreateInstance<TClient>())
{
}
public ClientProxy(Binding binding, EndpointAddress remoteAddress)
: this((TClient)Activator.CreateInstance(typeof(TClient), binding, remoteAddress))
{
}
public ClientProxy(TClient client)
{
Client = client;
}
#region Synchronous
public TResult Invoke<TResult>(Func<TResult> operation) =>
InvokeInternal<TResult>(operation, 0, null);
public TResult Invoke<T1, TResult>(Func<T1, TResult> operation, T1 arg) =>
InvokeInternal<TResult>(operation, 0, arg);
public TResult Invoke<T1, T2, TResult>(Func<T1, T2, TResult> operation, T1 arg1, T2 arg2) =>
InvokeInternal<TResult>(operation, 0, arg1, arg2);
public TResult Invoke<T1, T2, T3, TResult>(Func<T1, T2, T3, TResult> operation, T1 arg1, T2 arg2, T3 arg3) =>
InvokeInternal<TResult>(operation, 0, arg1, arg2, arg3);
public TResult Invoke<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, TResult> operation, T1 arg1, T2 arg2, T3 arg3, T4 arg4) =>
InvokeInternal<TResult>(operation, 0, arg1, arg2, arg3, arg4);
public TResult Invoke<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, TResult> operation, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg) =>
InvokeInternal<TResult>(operation, 0, arg1, arg2, arg3, arg4);
public TResult Invoke<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, TResult> operation, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg, T6 arg6) =>
InvokeInternal<TResult>(operation, 0, arg1, arg2, arg3, arg4, arg6);
private TResult InvokeInternal<TResult>(Delegate operation, int tryCount, params object[] args) =>
InvokeInternalAsync<TResult>(operation, tryCount, args).Result;
#endregion Synchronous
#region Asynchronous
public async Task<TResult> Invoke<TResult>(Func<Task<TResult>> operation) =>
await InvokeInternalAsync<TResult>(operation, 0, null);
public async Task<TResult> Invoke<T1, TResult>(Func<T1, Task<TResult>> operation, T1 arg) =>
await InvokeInternalAsync<TResult>(operation, 0, arg);
public async Task<TResult> Invoke<T1, T2, TResult>(Func<T1, T2, Task<TResult>> operation, T1 arg1, T2 arg2) =>
await InvokeInternalAsync<TResult>(operation, 0, arg1, arg2);
public async Task<TResult> Invoke<T1, T2, T3, TResult>(Func<T1, T2, T3, Task<TResult>> operation, T1 arg1, T2 arg2, T3 arg3) =>
await InvokeInternalAsync<TResult>(operation, 0, arg1, arg2, arg3);
public async Task<TResult> Invoke<T1, T2, T3, T4, TResult>(Func<T1, T2, T3, T4, Task<TResult>> operation, T1 arg1, T2 arg2, T3 arg3, T4 arg4) =>
await InvokeInternalAsync<TResult>(operation, 0, arg1, arg2, arg3, arg4);
public async Task<TResult> Invoke<T1, T2, T3, T4, T5, TResult>(Func<T1, T2, T3, T4, T5, Task<TResult>> operation, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg) =>
await InvokeInternalAsync<TResult>(operation, 0, arg1, arg2, arg3, arg4);
public async Task<TResult> Invoke<T1, T2, T3, T4, T5, T6, TResult>(Func<T1, T2, T3, T4, T5, T6, Task<TResult>> operation, T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg, T6 arg6) =>
await InvokeInternalAsync<TResult>(operation, 0, arg1, arg2, arg3, arg4, arg6);
private async Task<TResult> InvokeInternalAsync<TResult>(Delegate operation, int tryCount, params object[] args)
{
if (operation.Target != Client)
throw new ArgumentException(nameof(operation));
OnInvoke<TResult>(operation, args);
try
{
try
{
if (Client.State != CommunicationState.Opened)
Client.Open();
}
catch (ObjectDisposedException)
{
Client = (TClient)Activator.CreateInstance(typeof(TClient), Client.Endpoint.Binding, Client.Endpoint.Address);
}
var method = operation.Method;
TResult result;
if (method.ReturnType.IsAssignableFrom(typeof(Task<TResult>)))
{
var task = (Task<TResult>)operation.Method.Invoke(Client, args);
result = await task;
}
else
{
result = (TResult)operation.Method.Invoke(Client, args);
}
if (AutoClose)
Client.Close();
OnInvokeCompleted(operation, args, result);
return result;
}
catch (Exception ex)
{
Client.Abort();
var eventArgs = new ExceptionEventArgs(operation, ex, args, ++tryCount);
OnException(eventArgs);
if (!eventArgs.Handled)
throw ex;
if (eventArgs.Retry && tryCount < TryCount)
return await InvokeInternalAsync<TResult>(operation, tryCount, eventArgs.Arguments);
}
return default(TResult);
}
#endregion
protected virtual void OnInvoke<TResult>(Delegate operation, params object[] args)
{
}
/// <summary>
/// Called when an unhandled exception occurs.
/// </summary>
/// <typeparam name="TResult">The type of the expected result.</typeparam>
/// <param name="operation">The client operation that threw the exception.</param>
/// <param name="e">The exception.</param>
protected virtual void OnException(ExceptionEventArgs eventArgs)
{
if (Exception != null)
Exception(this, eventArgs);
if (!eventArgs.Handled)
{
var e = eventArgs.Exception;
if (e is CommunicationException)
{
}
else if (e is TimeoutException)
{
}
else if (e is FaultException)
{
}
else if (e is Exception)
{
}
}
}
public event EventHandler<ExceptionEventArgs> Exception;
/// <summary>
/// Called upon a successful invocation of the operation.
/// </summary>
/// <typeparam name="TResult">The type of the result received from server.</typeparam>
/// <param name="operation">The operation that was called.</param>
/// <param name="args">The arguments that were specified.</param>
/// <param name="result">The result received from server.</param>
protected virtual void OnInvokeCompleted<TResult>(Delegate operation, object[] args, TResult result)
{
}
public void Dispose()
{
if (Client.State != CommunicationState.Closed)
Client.Abort();
((IDisposable)Client).Dispose();
}
}
public class ExceptionEventArgs : EventArgs
{
public ExceptionEventArgs(Delegate operation, Exception exception, object[] args, int tryCount)
{
Operation = operation;
Exception = exception;
Arguments = args;
}
public Delegate Operation { get; }
public Exception Exception { get; }
public object[] Arguments { get; }
public bool Handled { get; set; }
/// <summary>
/// Gets or sets whether the operation should be retried.
/// </summary>
public bool Retry { get; set; }
/// <summary>
/// Gets the amount of times this operation has already been tried.
/// </summary>
public int TryCount { get; }
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment