These are chat archives for JasperFx/jasper

Jul 2016
Jeremy D. Miller
Jul 07 2016 20:59
@/all The FubuMVC reboot to “Jasper” is actively underway, finally. The first intermediate step is to make the current FubuMVC 3 codebase be “async by default” by changing the old IActionBehavior interface to:
public interface IActionBehavior
    Task Invoke();
    Task InvokePartial();
and just to feel like I accomplished something today (and I did dammit), the following code actually works:
public class ActionInvocation<TEndpoint>
    private static readonly MethodInfo ConfigureAwaitMethod = typeof(Task).GetMethod(nameof(Task.ConfigureAwait),
        new Type[] {typeof(bool)});

    private static readonly MethodInfo FubuRequestSet = typeof(IFubuRequest).GetMethod(nameof(IFubuRequest.Set),
        new Type[] {typeof(Type), typeof(object)});

    private readonly Func<TEndpoint, IFubuRequest, Task> _action;

    public ActionInvocation(MethodInfo method)
        var request = Expression.Parameter(typeof(IFubuRequest), "request");
        var endpoint = Expression.Parameter(typeof(TEndpoint), "endpoint");

        var inputs = method.GetParameters().Select(param => buildExpressionForParameter(request, param));

        Expression body = Expression.Call(endpoint, method, inputs);

        if (method.ReturnType != typeof(Task) && method.ReturnType.CanBeCastTo<Task>())
            var outputType = method.ReturnType.GetGenericArguments().Single();

            var awaiterMethod = method.ReturnType.GetMethod(nameof(Task.GetAwaiter));
            body = Expression.Call(body, awaiterMethod);

            var resultMethod = awaiterMethod.ReturnType.GetMethod(nameof(TaskAwaiter.GetResult));
            body = Expression.Call(body, resultMethod);

            body = setRequestExpression(outputType, request, body);

        else if (method.ReturnType != typeof(void) && method.ReturnType != typeof(Task))
            body = setRequestExpression(method.ReturnType, request, body);

        if (!body.Type.CanBeCastTo<Task>())
            var returnStatement = Expression.Constant(Task.CompletedTask);
            body = Expression.Block(body, returnStatement);

        var lambda = Expression.Lambda<Func<TEndpoint, IFubuRequest, Task>>(body, endpoint, request);

        _action = lambda.Compile();

    private Expression setRequestExpression(Type outputType, ParameterExpression request, Expression value)
        return Expression.Call(request, FubuRequestSet, Expression.Constant(outputType), value);

    private Expression buildExpressionForParameter(ParameterExpression request, ParameterInfo parameter)
        var method = typeof(IFubuRequest).GetMethod(nameof(IFubuRequest.Get), new Type[0]).MakeGenericMethod(parameter.ParameterType);

        return Expression.Call(request, method);

    public Task Invoke(IFubuRequest request, TEndpoint endpoint)
        return _action(endpoint, request);