public Task MapAsync(Func<IContainer> container, Type type, MethodInfo method, Routing.Route route) { container.ThrowIfNull("container"); type.ThrowIfNull("type"); method.ThrowIfNull("method"); route.ThrowIfNull("route"); bool methodReturnTypeImplementsIResponse = method.ReturnType.ImplementsInterface<IResponse>(); bool methodReturnTypeIsTaskT = method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>); bool methodReturnTypeIsVoid = method.ReturnType == typeof(void); if (methodReturnTypeImplementsIResponse) { ParameterInfo[] parameterInfos = method.GetParameters(); ParameterExpression instanceParameterExpression = Expression.Parameter(typeof(object), "instance"); ParameterExpression parametersParameterExpression = Expression.Parameter(typeof(object[]), "parameters"); UnaryExpression unaryExpression = Expression.Convert( Expression.Call( Expression.Convert(instanceParameterExpression, type), method, parameterInfos.Select((arg, index) => Expression.Convert( Expression.ArrayIndex(parametersParameterExpression, Expression.Constant(index)), arg.ParameterType))), typeof(IResponse)); Func<object, object[], IResponse> @delegate = Expression.Lambda<Func<object, object[], IResponse>>(unaryExpression, instanceParameterExpression, parametersParameterExpression).Compile(); route.RespondWith( async context => { object instance; try { instance = container().GetInstance(type); } catch (Exception exception) { throw new ApplicationException(String.Format("Unable to resolve instance of type {0}.", type.FullName), exception); } if (instance == null) { throw new ApplicationException(String.Format("Unable to resolve instance of type {0}.", type.FullName)); } var parameterValueRetriever = new ParameterValueRetriever(_parameterMappers); object[] parameterValues = (await parameterValueRetriever.GetParameterValuesAsync(context, type, method)).ToArray(); var mappedDelegateContexts = new List<IMappedDelegateContext>(); try { mappedDelegateContexts.AddRange(_contextFactories.Select(arg => arg.CreateContext(context, type, method)).Where(arg => arg != null)); IResponse response = @delegate(instance, parameterValues); foreach (IMappedDelegateContext mappedDelegateContext in mappedDelegateContexts) { mappedDelegateContext.Complete(); } return response; } finally { foreach (IMappedDelegateContext mappedDelegateContext in mappedDelegateContexts) { mappedDelegateContext.Dispose(); } } }, method.ReturnType); } else if (methodReturnTypeIsTaskT) { ParameterInfo[] parameterInfos = method.GetParameters(); ParameterExpression instanceParameterExpression = Expression.Parameter(typeof(object), "instance"); ParameterExpression parametersParameterExpression = Expression.Parameter(typeof(object[]), "parameters"); Type methodGenericArgumentType = method.ReturnType.GetGenericArguments()[0]; MethodInfo upcastMethodInfo = typeof(TaskExtensions) .GetMethod("Upcast", BindingFlags.Static | BindingFlags.Public) .MakeGenericMethod(methodGenericArgumentType, typeof(IResponse)); UnaryExpression unaryExpression = Expression.Convert( Expression.Call( upcastMethodInfo, Expression.Call( Expression.Convert(instanceParameterExpression, type), method, parameterInfos.Select((arg, index) => Expression.Convert( Expression.ArrayIndex(parametersParameterExpression, Expression.Constant(index)), arg.ParameterType)))), upcastMethodInfo.ReturnType); Func<object, object[], Task<IResponse>> @delegate = Expression.Lambda<Func<object, object[], Task<IResponse>>>(unaryExpression, instanceParameterExpression, parametersParameterExpression).Compile(); route.RespondWith( async context => { object instance; try { instance = container().GetInstance(type); } catch (Exception exception) { throw new ApplicationException(String.Format("Unable to resolve instance of type {0}.", type.FullName), exception); } if (instance == null) { throw new ApplicationException(String.Format("Unable to resolve instance of type {0}.", type.FullName)); } var parameterValueRetriever = new ParameterValueRetriever(_parameterMappers); object[] parameterValues = (await parameterValueRetriever.GetParameterValuesAsync(context, type, method)).ToArray(); var mappedDelegateContexts = new List<IMappedDelegateContext>(); try { mappedDelegateContexts.AddRange(_contextFactories.Select(arg => arg.CreateContext(context, type, method)).Where(arg => arg != null)); IResponse response = await @delegate(instance, parameterValues); foreach (IMappedDelegateContext mappedDelegateContext in mappedDelegateContexts) { mappedDelegateContext.Complete(); } return response; } finally { foreach (IMappedDelegateContext mappedDelegateContext in mappedDelegateContexts) { mappedDelegateContext.Dispose(); } } }, methodGenericArgumentType); } else if (methodReturnTypeIsVoid) { ParameterInfo[] parameterInfos = method.GetParameters(); ParameterExpression instanceParameterExpression = Expression.Parameter(typeof(object), "instance"); ParameterExpression parametersParameterExpression = Expression.Parameter(typeof(object[]), "parameters"); MethodCallExpression methodCallExpression = Expression.Call( Expression.Convert(instanceParameterExpression, type), method, parameterInfos.Select((arg, index) => Expression.Convert( Expression.ArrayIndex(parametersParameterExpression, Expression.Constant(index)), arg.ParameterType))); Action<object, object[]> @delegate = Expression.Lambda<Action<object, object[]>>(methodCallExpression, instanceParameterExpression, parametersParameterExpression).Compile(); route.RespondWithNoContent( async context => { object instance; try { instance = container().GetInstance(type); } catch (Exception exception) { throw new ApplicationException(String.Format("Unable to resolve instance of type {0}.", type.FullName), exception); } if (instance == null) { throw new ApplicationException(String.Format("Unable to resolve instance of type {0}.", type.FullName)); } var parameterValueRetriever = new ParameterValueRetriever(_parameterMappers); object[] parameterValues = (await parameterValueRetriever.GetParameterValuesAsync(context, type, method)).ToArray(); var mappedDelegateContexts = new List<IMappedDelegateContext>(); try { mappedDelegateContexts.AddRange(_contextFactories.Select(arg => arg.CreateContext(context, type, method)).Where(arg => arg != null)); @delegate(instance, parameterValues); foreach (IMappedDelegateContext mappedDelegateContext in mappedDelegateContexts) { mappedDelegateContext.Complete(); } } finally { foreach (IMappedDelegateContext mappedDelegateContext in mappedDelegateContexts) { mappedDelegateContext.Dispose(); } } }); } else { throw new ApplicationException(String.Format("The return type of {0}.{1} must implement {2} or be a {3} whose generic type argument implements {2}.", type.FullName, method.Name, typeof(IResponse).Name, typeof(Task<>))); } return Task.Factory.Empty(); }
public void Map(Func<IContainer> container, Type type, MethodInfo method, Routing.Route route) { container.ThrowIfNull("container"); type.ThrowIfNull("type"); method.ThrowIfNull("method"); route.ThrowIfNull("route"); if (method.ReturnType == typeof(void)) { route.RespondWithNoContent(); return; } if (!method.ReturnType.ImplementsInterface<IResponse>()) { throw new ApplicationException(String.Format("The return type of '{0}.{1}' does not implement '{2}'.", type.FullName, method.Name, typeof(IResponse).Name)); } route.RespondWith( request => { object instance; try { instance = container().GetInstance(type); } catch (Exception exception) { throw new ApplicationException(String.Format("Unable to resolve instance of type '{0}'.", type.FullName), exception); } if (instance == null) { throw new ApplicationException(String.Format("Unable to resolve instance of type '{0}'.", type.FullName)); } var parameterValueRetriever = new ParameterValueRetriever(_parameterMappers); IEnumerable<object> parameterValues = parameterValueRetriever.GetParameterValues(request, type, method); return (IResponse)method.Invoke(instance, parameterValues.ToArray()); }, method.ReturnType); }