Last active
June 19, 2017 11:20
-
-
Save quexy/c987ca1136fd36d067cbb45fa424ac29 to your computer and use it in GitHub Desktop.
Cancelable wrapper for Begin--End-style async methods
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; | |
using System.Linq.Expressions; | |
using System.Threading; | |
namespace Cancelable | |
{ | |
public struct CancelableActionCall<TS> | |
{ | |
private readonly TS _svc; | |
private readonly CancellationToken _token; | |
public CancelableActionCall(TS svc, CancellationToken token) | |
{ | |
this._svc = svc; | |
this._token = token; | |
} | |
public CancelableFunctionCall<TS, TR> ForResult<TR>() | |
{ | |
return new CancelableFunctionCall<TS, TR>(_svc, _token); | |
} | |
#region public void Call(Expression<Func<TS, Action<[T1-TF]>>>, [T1-TF]); | |
public void Call( | |
Expression<Func<TS, Action>> func) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { }, _token); | |
} | |
public void Call<T1>( | |
Expression<Func<TS, Action<T1>>> func, | |
T1 arg1) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1 }, _token); | |
} | |
public void Call<T1, T2>( | |
Expression<Func<TS, Action<T1, T2>>> func, | |
T1 arg1, T2 arg2) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2 }, _token); | |
} | |
public void Call<T1, T2, T3>( | |
Expression<Func<TS, Action<T1, T2, T3>>> func, | |
T1 arg1, T2 arg2, T3 arg3) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3 }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5 }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TD>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC, TD argD) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC, argD }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TD, TE>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC, TD argD, TE argE) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC, argD, argE }, _token); | |
} | |
public void Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TD, TE, TF>( | |
Expression<Func<TS, Action<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC, TD argD, TE argE, TF argF) | |
{ | |
CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC, argD, argE, argF }, _token); | |
} | |
#endregion | |
} | |
} |
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; | |
using System.Linq.Expressions; | |
using System.Threading; | |
namespace Cancelable | |
{ | |
public struct CancelableFunctionCall<TS, TR> | |
{ | |
private readonly TS _svc; | |
private readonly CancellationToken _token; | |
public CancelableFunctionCall(TS svc, CancellationToken token) | |
{ | |
this._svc = svc; | |
this._token = token; | |
} | |
#region public TR Call(Expression<Func<TS, Func<[T1-TF], TR>>>, [T1-TF]); | |
public TR Call( | |
Expression<Func<TS, Func<TR>>> func) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { }, _token); | |
} | |
public TR Call<T1>( | |
Expression<Func<TS, Func<T1, TR>>> func, | |
T1 arg1) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1 }, _token); | |
} | |
public TR Call<T1, T2>( | |
Expression<Func<TS, Func<T1, T2, TR>>> func, | |
T1 arg1, T2 arg2) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2 }, _token); | |
} | |
public TR Call<T1, T2, T3>( | |
Expression<Func<TS, Func<T1, T2, T3, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9 }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TD>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC, TD argD) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC, argD }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TD, TE>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC, TD argD, TE argE) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC, argD, argE }, _token); | |
} | |
public TR Call<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TC, TD, TE, TF>( | |
Expression<Func<TS, Func<T1, T2, T3, T4, T5, T6, T7, T8, T9, TA, TB, TR>>> func, | |
T1 arg1, T2 arg2, T3 arg3, T4 arg4, T5 arg5, T6 arg6, T7 arg7, T8 arg8, T9 arg9, TA argA, TB argB, TC argC, TD argD, TE argE, TF argF) | |
{ | |
return (TR)CancelableMethodCall.CallAsync(_svc, func, new object[] { arg1, arg2, arg3, arg4, arg5, arg6, arg7, arg8, arg9, argA, argB, argC, argD, argE, argF }, _token); | |
} | |
#endregion | |
} | |
} |
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; | |
using System.Linq; | |
using System.Linq.Expressions; | |
using System.Threading; | |
using System.Threading.Tasks; | |
using System.Reflection; | |
using System.ServiceModel; | |
namespace Cancelable | |
{ | |
internal static class CancelableMethodCall | |
{ | |
const string NoMethodMessage = "Could not determine method"; | |
public static object CallAsync(object instance, Expression selector, object[] funcArgs, CancellationToken cancellation) | |
{ | |
var lambda = selector as LambdaExpression; | |
if (lambda == null) throw new InvalidOperationException(NoMethodMessage); | |
var convert = lambda.Body as UnaryExpression; | |
if (convert == null) throw new InvalidOperationException(NoMethodMessage); | |
var call = convert.Operand as MethodCallExpression; | |
if (call == null) throw new InvalidOperationException(NoMethodMessage); | |
var cons = call.Object as ConstantExpression; | |
if (cons == null) cons = call.Arguments[2] as ConstantExpression; | |
if (cons == null) throw new InvalidOperationException(NoMethodMessage); | |
var method = cons.Value as MethodInfo; | |
if (method == null) throw new InvalidOperationException(NoMethodMessage); | |
var beginFuncArgs = funcArgs.Concat(new object[] { null, null }).ToArray(); | |
var beginArgTypes = method.GetParameters().Select(pi => pi.ParameterType) | |
.Concat(new[] { typeof(AsyncCallback), typeof(object) }).ToArray(); | |
var beginCallMethod = instance.GetType().GetMethod("Begin" + method.Name, beginArgTypes); | |
if (beginCallMethod == null) throw new InvalidOperationException(NoMethodMessage); | |
var endCallMethod = instance.GetType().GetMethod("End" + method.Name, new[] { typeof(IAsyncResult) }); | |
if (endCallMethod == null) throw new InvalidOperationException(NoMethodMessage); | |
try | |
{ | |
var asyncResult = (IAsyncResult)beginCallMethod.Invoke(instance, beginFuncArgs); | |
if (!asyncResult.IsCompleted) WaitHandle.WaitAny(new[] { asyncResult.AsyncWaitHandle, cancellation.WaitHandle }); | |
if (!cancellation.IsCancellationRequested) return endCallMethod.Invoke(instance, new object[] { asyncResult }); | |
StartOperationCleanupTask(instance, endCallMethod, asyncResult); cancellation.ThrowIfCancellationRequested(); | |
throw new InvalidOperationException("Cancellation has been issued but exception was not thrown"); | |
} | |
catch (TargetInvocationException tiex) | |
{ | |
if (tiex.InnerException == null) throw; | |
// unpack exception so that callers know more about what has happened | |
throw UnpackException(tiex.InnerException); | |
} | |
} | |
private static Exception UnpackException(Exception ex) | |
{ | |
var fex = ex as FaultException; if (fex != null) return UnpackFaultException(fex); | |
try { return (Exception)Activator.CreateInstance(ex.GetType(), ex.Message, ex); } | |
catch (Exception) { return new CommunicationException("The service action failed", ex); } | |
} | |
private static Exception UnpackFaultException(FaultException fex) | |
{ | |
if (!fex.GetType().IsGenericType) return new FaultException(fex.Reason, fex.Code, fex.Action); | |
var detail = fex.GetType().GetProperty("Detail").GetValue(fex, null); | |
return (Exception)Activator.CreateInstance(fex.GetType(), detail, fex.Reason, fex.Code, fex.Action); | |
} | |
private static Task StartOperationCleanupTask(object instance, MethodInfo endCallMethod, IAsyncResult asyncResult) | |
{ | |
return Task.Factory.StartNew(() => | |
{ | |
try | |
{ | |
// wait and end call to avoid leaking memory | |
endCallMethod.Invoke(instance, new object[] { asyncResult }); | |
} | |
catch | |
{ | |
/* swallow all errors; method is for cleanup purposes only */ | |
} | |
}); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment