Skip to content

Instantly share code, notes, and snippets.

@quexy
Last active June 19, 2017 11:20
Show Gist options
  • Save quexy/c987ca1136fd36d067cbb45fa424ac29 to your computer and use it in GitHub Desktop.
Save quexy/c987ca1136fd36d067cbb45fa424ac29 to your computer and use it in GitHub Desktop.
Cancelable wrapper for Begin--End-style async methods
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
}
}
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
}
}
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