Example #1
0
        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);
        }