/ThirdParty/Src/protobuf-net/ServiceModel/Client/ProtoClient.cs

https://github.com/rkabir/inbox2_desktop · C# · 229 lines · 136 code · 23 blank · 70 comment · 27 complexity · fb719def7f10f9191ddd15e077f0741a MD5 · raw file

  1. using System;
  2. using System.Reflection;
  3. using System.Net;
  4. using System.Threading;
  5. #if NET_3_0
  6. using System.ServiceModel;
  7. #endif
  8. namespace ProtoBuf.ServiceModel.Client
  9. {
  10. /// <summary>
  11. /// Provides transport-independent wrapper logic for
  12. /// managing RPC calls to the server.
  13. /// </summary>
  14. /// <typeparam name="TService">The service contract that the client represents.</typeparam>
  15. public class ProtoClient<TService> : IDisposable where TService : class
  16. {
  17. private int timeout = 30000;
  18. /// <summary>
  19. /// Gets or sets the timeout (in milliseconds) for synchronous RPC operations.
  20. /// </summary>
  21. public int Timeout
  22. {
  23. get { return timeout; }
  24. set { timeout = value; }
  25. }
  26. private ITransport transport;
  27. /// <summary>
  28. /// Gets the transport mechanism associated with the client.
  29. /// </summary>
  30. public ITransport Transport { get { return transport; } }
  31. private bool IsDisposed { get { return transport == null; } }
  32. private void CheckDisposed()
  33. {
  34. if (IsDisposed) throw new ObjectDisposedException(ToString());
  35. }
  36. /// <summary>
  37. /// Releases any resources associated with the client.
  38. /// </summary>
  39. public void Dispose()
  40. {
  41. if(transport != null)
  42. {
  43. transport.Dispose();
  44. transport = null;
  45. }
  46. if (waitEvent != null)
  47. {
  48. waitEvent.Set(); // release any pending thread
  49. waitEvent.Close();
  50. waitEvent = null;
  51. }
  52. }
  53. /// <summary>
  54. /// Create a new client object.
  55. /// </summary>
  56. /// <param name="transport">The transport implementation to use.</param>
  57. public ProtoClient(ITransport transport)
  58. {
  59. if (transport == null) throw new ArgumentNullException("transport");
  60. if (!typeof(TService).IsInterface) throw new ArgumentException("Services must be expressed as interfaces");
  61. this.transport = transport;
  62. }
  63. /// <summary>
  64. /// Begins an RPC invokation asynchrononously.
  65. /// </summary>
  66. /// <param name="methodName">The name of the method (on the service interface) to invoke.</param>
  67. /// <param name="args">The request payload.</param>
  68. /// <param name="callback">The operation to perform when a response is received.</param>
  69. public void InvokeAsync(
  70. string methodName,
  71. Action<AsyncResult> callback,
  72. params object[] args)
  73. {
  74. InvokeAsync(ResolveMethod(methodName), callback, args);
  75. }
  76. private MethodInfo ResolveMethod(string methodName)
  77. {
  78. CheckDisposed();
  79. if (string.IsNullOrEmpty(methodName)) throw new ArgumentNullException("methodName");
  80. MethodInfo method = typeof(TService).GetMethod(methodName);
  81. if (method == null) throw new ArgumentException("Method " + methodName
  82. + " not found on service " + typeof(TService).Name);
  83. return method;
  84. }
  85. /// <summary>
  86. /// Performs an RPC invokation synchrononously.
  87. /// </summary>
  88. /// <param name="methodName">The name of the method (on the service interface) to invoke.</param>
  89. /// <param name="args">The request payload.</param>
  90. /// <returns>The response payload.</returns>
  91. public object Invoke (
  92. string methodName,
  93. params object[] args)
  94. {
  95. return Invoke(ResolveMethod(methodName), args);
  96. }
  97. private ManualResetEvent waitEvent = new ManualResetEvent(false);
  98. /// <summary>
  99. /// Performs an RPC invokation synchrononously.
  100. /// </summary>
  101. /// <param name="method">The method (on the service interface) to invoke.</param>
  102. /// <param name="args">The request payload.</param>
  103. /// <returns>The response payload.</returns>
  104. public object Invoke(
  105. MethodInfo method,
  106. params object[] args)
  107. {
  108. CheckDisposed();
  109. if (args == null) throw new ArgumentNullException("args");
  110. waitEvent.Reset();
  111. AsyncResult result = null;
  112. SendRequestAsync(method, delegate(AsyncResult ar)
  113. {
  114. result = ar;
  115. waitEvent.Set();
  116. }, args);
  117. if (!waitEvent.WaitOne(Timeout
  118. #if !SILVERLIGHT
  119. , false
  120. #endif
  121. ))
  122. {
  123. throw new TimeoutException("Timeout from " + method.Name);
  124. }
  125. #if !CF2
  126. Thread.MemoryBarrier();
  127. #endif
  128. CheckDisposed();
  129. return result();
  130. }
  131. /// <summary>
  132. /// Begins an RPC invokation asynchrononously.
  133. /// </summary>
  134. /// <param name="method">The method (on the service interface) to invoke.</param>
  135. /// <param name="args">The request payload.</param>
  136. /// <param name="callback">The operation to perform when a response is received.</param>
  137. public void InvokeAsync (
  138. MethodInfo method,
  139. Action<AsyncResult> callback,
  140. params object[] args)
  141. {
  142. SendRequestAsync(method, callback, args);
  143. }
  144. /// <summary>
  145. /// Identify the action to use for a given method.
  146. /// </summary>
  147. /// <param name="method">The method requested.</param>
  148. /// <returns>The action to use.</returns>
  149. protected virtual string ResolveAction(MethodInfo method)
  150. {
  151. return RpcUtils.GetActionName(method);
  152. }
  153. /// <summary>
  154. /// Identify the service to use for a given method.
  155. /// </summary>
  156. /// <param name="serviceType">The service requested.</param>
  157. /// <returns>The service to use.</returns>
  158. protected virtual string ResolveService(Type serviceType)
  159. {
  160. return RpcUtils.GetServiceName(serviceType);
  161. }
  162. private void SendRequestAsync(
  163. MethodInfo method,
  164. Action<AsyncResult> callback,
  165. object[] args)
  166. {
  167. CheckDisposed();
  168. if(args == null) throw new ArgumentNullException("args");
  169. if(method == null) throw new ArgumentNullException("method");
  170. if (method.IsGenericMethod || method.IsGenericMethodDefinition) throw new InvalidOperationException("Cannot process generic method: " + method.Name);
  171. if (method.DeclaringType != typeof(TService)) throw new ArgumentException(method.Name + " is not defined on service " + typeof(TService).Name, "method");
  172. ParameterInfo[] parameters = method.GetParameters();
  173. if(parameters.Length != args.Length)
  174. {
  175. throw new InvalidOperationException("Parameter mismatch calling " + method.Name);
  176. }
  177. ServiceRequest reqWrapper = new ServiceRequest(
  178. ResolveService(typeof(TService)), ResolveAction(method), method, args, callback, callback);
  179. transport.SendRequestAsync(reqWrapper);
  180. }
  181. /*
  182. private void GotResponse(ServiceRequest response)
  183. {
  184. ServiceRequestCallback callback;
  185. if (response != null && !IsDisposed
  186. && (callback = (ServiceRequestCallback)response.UserState) != null)
  187. {
  188. callback(response.ResponseObject, response.Exception);
  189. }
  190. }
  191. * */
  192. /// <summary>
  193. /// Raised when an error occurs processing RPC calls.
  194. /// </summary>
  195. public event EventHandler<ExceptionEventArgs> ServiceException;
  196. /// <summary>
  197. /// Signals that an error occured processing RPC calls.
  198. /// </summary>
  199. /// <param name="exception">The error details.</param>
  200. protected virtual void OnException(Exception exception)
  201. {
  202. EventHandler<ExceptionEventArgs> handler = ServiceException;
  203. if(handler != null) handler(this, new ExceptionEventArgs(exception));
  204. }
  205. }
  206. }