/DotNet/Source/ProtoBufRemote/RpcMethodDescriptor.cs

https://code.google.com/p/protobuf-remote/ · C# · 187 lines · 162 code · 23 blank · 2 comment · 80 complexity · b805e00a231740b1482ebfff11cd6fec MD5 · raw file

  1. using System;
  2. using System.Collections.Generic;
  3. using System.Diagnostics;
  4. using System.Reflection;
  5. using System.Linq;
  6. namespace ProtoBufRemote
  7. {
  8. internal class RpcMethodDescriptor
  9. {
  10. private readonly string name;
  11. private readonly string serviceName;
  12. private MethodInfo syncMethodInfo;
  13. private MethodInfo asyncBeginMethodInfo;
  14. private MethodInfo asyncEndMethodInfo;
  15. private Type[] parameterTypes;
  16. private Type returnType;
  17. public RpcMethodDescriptor(MethodInfo methodInfo, RpcMethodAttribute attr, string serviceName)
  18. {
  19. this.serviceName = serviceName;
  20. Type[] methodParamTypes = methodInfo.GetParameters().Select(p => p.ParameterType).ToArray();
  21. if (methodInfo.Name.StartsWith("Begin") && methodParamTypes.Length >= 2
  22. && methodInfo.ReturnType == typeof(IAsyncResult)
  23. && methodParamTypes[methodParamTypes.Length - 2] == typeof(AsyncCallback)
  24. && methodParamTypes[methodParamTypes.Length - 1] == typeof(object))
  25. {
  26. asyncBeginMethodInfo = methodInfo;
  27. parameterTypes = methodParamTypes.Take(methodParamTypes.Length-2).ToArray();
  28. if (attr != null && attr.Name != null)
  29. name = attr.Name;
  30. else
  31. name = methodInfo.Name.Substring(5);
  32. }
  33. else if (methodInfo.Name.StartsWith("End") && methodParamTypes.Length == 1
  34. && methodParamTypes[0] == typeof(IAsyncResult))
  35. {
  36. asyncEndMethodInfo = methodInfo;
  37. returnType = methodInfo.ReturnType;
  38. if (attr != null && attr.Name != null)
  39. name = attr.Name;
  40. else
  41. name = methodInfo.Name.Substring(3);
  42. }
  43. else
  44. {
  45. syncMethodInfo = methodInfo;
  46. parameterTypes = methodParamTypes;
  47. returnType = methodInfo.ReturnType;
  48. if (attr != null && attr.Name != null)
  49. name = attr.Name;
  50. else
  51. name = methodInfo.Name;
  52. }
  53. //verify parameters
  54. if (parameterTypes != null)
  55. {
  56. foreach (Type paramType in parameterTypes)
  57. {
  58. if (!ParameterConverter.IsSupportedType(paramType))
  59. throw new ArgumentException(
  60. String.Format(
  61. "Parameter of type '{0}' on method '{1}' in service '{2}' is not a supported type.",
  62. paramType, methodInfo.Name, serviceName));
  63. }
  64. }
  65. //verify return type
  66. if (returnType != null && returnType != typeof(void))
  67. {
  68. if (!ParameterConverter.IsSupportedType(returnType))
  69. throw new ArgumentException(
  70. String.Format("Return type '{0}' of method '{1}' in service '{2}' is not a supported type",
  71. returnType, name, serviceName));
  72. }
  73. }
  74. public string Name { get { return name; } }
  75. public bool HasAsyncDeclarations { get { return (asyncBeginMethodInfo != null); } }
  76. public MethodInfo SyncCallMethod { get { return syncMethodInfo; } }
  77. public MethodInfo AsyncBeginCallMethod { get { return asyncBeginMethodInfo; } }
  78. public MethodInfo AsyncEndCallMethod { get { return asyncEndMethodInfo; } }
  79. public Type[] ParameterTypes { get { return parameterTypes; } }
  80. public Type ReturnType { get { return returnType; } }
  81. public void Merge(RpcMethodDescriptor method)
  82. {
  83. Debug.Assert(method.name == name && method.serviceName == serviceName);
  84. if (returnType != null && method.returnType != null && returnType != method.returnType)
  85. throw new ArgumentException(
  86. String.Format("Invalid declarations for method '{0}', the return types are not consistent.", name));
  87. if (parameterTypes != null && method.parameterTypes != null
  88. && !parameterTypes.SequenceEqual(method.parameterTypes))
  89. throw new ArgumentException(
  90. String.Format("Invalid declarations for method '{0}', the parameter types are not consistent.", name));
  91. if (syncMethodInfo != null && method.syncMethodInfo != null)
  92. throw new ArgumentException(
  93. String.Format("Invalid declarations for method '{0}', more than one synchronous call found.", name));
  94. if (asyncBeginMethodInfo != null && method.asyncBeginMethodInfo != null)
  95. throw new ArgumentException(
  96. String.Format("Invalid declarations for method '{0}', more than one begin async call found.", name));
  97. if (asyncEndMethodInfo != null && method.asyncEndMethodInfo != null)
  98. throw new ArgumentException(
  99. String.Format("Invalid declarations for method '{0}', more than one end async call found.", name));
  100. returnType = returnType ?? method.returnType;
  101. parameterTypes = parameterTypes ?? method.parameterTypes;
  102. syncMethodInfo = syncMethodInfo ?? method.syncMethodInfo;
  103. asyncBeginMethodInfo = asyncBeginMethodInfo ?? method.asyncBeginMethodInfo;
  104. asyncEndMethodInfo = asyncEndMethodInfo ?? method.asyncEndMethodInfo;
  105. }
  106. public bool IsAsyncDeclarationValid()
  107. {
  108. if (asyncBeginMethodInfo != null && asyncEndMethodInfo != null)
  109. return true;
  110. if (asyncBeginMethodInfo == null && asyncEndMethodInfo == null)
  111. return true;
  112. return false;
  113. }
  114. public void Invoke(object service, IList<RpcMessage.Parameter> parameters, RpcMessage resultMessage)
  115. {
  116. ParameterInfo[] parameterInfos = syncMethodInfo.GetParameters();
  117. if (parameterInfos.Length != parameters.Count)
  118. {
  119. if (resultMessage != null)
  120. {
  121. resultMessage.ResultMessage.IsFailed = true;
  122. resultMessage.ResultMessage.ErrorMessage = String.Format(
  123. "Wrong number of parameters for method '{1}' in service '{0}'", serviceName, Name);
  124. }
  125. return;
  126. }
  127. var invokeParameters = new object[parameters.Count];
  128. for (int i=0; i<parameters.Count; ++i)
  129. {
  130. string errorMsg;
  131. if (!ParameterConverter.FromMessage(parameters[i], parameterInfos[i].ParameterType,
  132. out invokeParameters[i], out errorMsg))
  133. {
  134. if (resultMessage != null)
  135. {
  136. resultMessage.ResultMessage.IsFailed = true;
  137. resultMessage.ResultMessage.ErrorMessage = String.Format(
  138. "Parameter {0} could not be converted to expected type '{1}' for method '{2}' in service '{3}' ({4}).",
  139. i, parameterInfos[i].ParameterType, Name, serviceName, errorMsg);
  140. }
  141. return;
  142. }
  143. }
  144. object result = syncMethodInfo.Invoke(service, invokeParameters);
  145. if (resultMessage != null)
  146. {
  147. RpcMessage.Parameter resultParameter;
  148. string errorMsg;
  149. if (ParameterConverter.ToMessage(result, out resultParameter, out errorMsg))
  150. {
  151. resultMessage.ResultMessage.CallResult = resultParameter;
  152. }
  153. else
  154. {
  155. resultMessage.ResultMessage.IsFailed = true;
  156. resultMessage.ResultMessage.ErrorMessage = String.Format(
  157. "Result type {0} could not be converted to a message for method '{1}' in service '{2}' ({3}).",
  158. syncMethodInfo.ReturnType, Name, serviceName, errorMsg);
  159. return;
  160. }
  161. }
  162. }
  163. }
  164. }