internal async Task <object> CallMethodAsync(RpcCall call) { var response = await _connection.SendMessageAsync(call); if (response.Status != RequestStatus.Success) { throw new InvalidOperationException( $"The remote procedure call to '{call.MethodName}' failed ({response.Status})"); } if (response.Response is RpcReturn returnMessage) { if (!returnMessage.IsSuccessful) { throw new InvalidOperationException($"The remote procedure call to '{call.MethodName}' failed " + $"({returnMessage.Error ?? "no details"})"); } return(returnMessage.Value); } throw new InvalidOperationException( $"The remote procedure call to '{call.MethodName}' failed (invalid response message)"); }
public static async Task <RpcReturn> InvokeMethodAsync(object rpcTarget, RpcConnection connection, RpcCall call) { // Current limitations // - no support for overloaded methods if (string.IsNullOrEmpty(call.MethodName)) { return(RpcReturn.Faulted("No method name specified")); } var method = rpcTarget.GetType().GetMethod(call.MethodName); if (method == null) { return(RpcReturn.Faulted( $"No method with name '{call.MethodName}' could be found (attempted call: {call.ToString()})")); } // Parameter check var formalParams = method.GetParameters(); var actualParams = call.Parameters.ToList(); if (call.Parameters.Length > formalParams.Length) { return(RpcReturn.Faulted("Parameter mismatch: Too many arguments specified " + $"(attempted call: {call.ToString()}, local method: {method.ToDescriptionString()})")); } for (var i = 0; i < formalParams.Length; i++) { var formalParam = formalParams[i]; var actualParam = (actualParams.Count <= i) ? null : actualParams[i]; if (actualParam == null) { actualParams.Add(actualParam = Type.Missing); } else if (!formalParam.ParameterType.IsAssignableFrom(actualParam.GetType())) { return(RpcReturn.Faulted("Parameter mismatch: Got value of type " + $"'{actualParam.GetType().FullName}' for parameter " + $"'{formalParam.ParameterType.FullName} {formalParam.Name}' " + $"(attempted call: {call.ToString()}, local method: {method.ToDescriptionString()})")); } if (formalParam.CustomAttributes.Any(a => a.AttributeType == typeof(RpcCallerAttribute)) && formalParam.ParameterType.IsAssignableFrom(typeof(RpcConnection))) { // Insert RpcProxy into [RpcCaller]-annotated parameter actualParams[i] = connection; } else if (actualParam == Type.Missing && !formalParam.IsOptional) { return(RpcReturn.Faulted("Parameter mismatch: Not enough parameters specified " + $"(attempted call: {call.ToString()}, local method: {method.ToDescriptionString()})")); } } // Invoke method try { var returnValue = method.Invoke(rpcTarget, actualParams.ToArray()); if (returnValue is Task) { await(Task) returnValue; if (returnValue.GetType().GetGenericTypeDefinition() == typeof(Task <>) && returnValue.GetType().GetGenericArguments()[0].Name != "VoidTaskResult") { returnValue = ((dynamic)returnValue).Result; } else { returnValue = null; } } return(RpcReturn.Success(returnValue)); } catch (Exception e) { return(RpcReturn.Faulted($"Local execution failed (exception: {e.ToString()})")); } }
internal async Task <object[]> CallMethodAsync(RpcCall call) { var results = await Task.WhenAll(_proxies.Select(proxy => proxy.CallMethodAsync(call))); return(results); }