Last active
December 3, 2015 20:15
-
-
Save weitzhandler/be9e8784aac367b43d39 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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