PageRenderTime 50ms CodeModel.GetById 22ms RepoModel.GetById 0ms app.codeStats 0ms

/Candidate/NodeService/NodeService/Endpoints/StrongTypedNodeEndpointClientBuilder.cs

#
C# | 248 lines | 227 code | 21 blank | 0 comment | 22 complexity | b8032742a0ac5fbd5ea38e464f0404ef MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Linq;
  4. using System.Text;
  5. using System.CodeDom;
  6. using System.Reflection;
  7. using System.CodeDom.Compiler;
  8. using System.Threading.Tasks;
  9. using System.Xml.Linq;
  10. namespace NodeService.Endpoints
  11. {
  12. public static class StrongTypedNodeEndpointClientBuilder
  13. {
  14. private static Dictionary<Type, Type> clientImplementations = new Dictionary<Type, Type>();
  15. private static CodeExpression PassParameterNames(MethodInfo methodInfo)
  16. {
  17. return new CodeArrayCreateExpression(
  18. typeof(string),
  19. methodInfo
  20. .GetParameters()
  21. .Select(p => new CodePrimitiveExpression(p.Name))
  22. .ToArray()
  23. );
  24. }
  25. private static CodeExpression PassParameterValues(MethodInfo methodInfo)
  26. {
  27. return new CodeArrayCreateExpression(
  28. typeof(object),
  29. methodInfo
  30. .GetParameters()
  31. .Select(p => new CodeArgumentReferenceExpression(p.Name))
  32. .ToArray()
  33. );
  34. }
  35. private static CodeExpression InvokeExecute(MethodInfo methodInfo)
  36. {
  37. CodeExpression execution = new CodeMethodInvokeExpression(
  38. new CodeThisReferenceExpression(),
  39. "Execute",
  40. new CodeExpression[] {
  41. new CodePrimitiveExpression(methodInfo.Name),
  42. new CodeTypeOfExpression(methodInfo.ReturnType),
  43. PassParameterNames(methodInfo),
  44. PassParameterValues(methodInfo),
  45. }
  46. .ToArray()
  47. );
  48. return execution;
  49. }
  50. private static CodeExpression InvokeExecuteTask(MethodInfo methodInfo)
  51. {
  52. CodeExpression execution = new CodeMethodInvokeExpression(
  53. methodInfo.ReturnType == typeof(Task) ?
  54. new CodeMethodReferenceExpression(
  55. new CodeThisReferenceExpression(),
  56. "ExecuteTask"
  57. ) :
  58. new CodeMethodReferenceExpression(
  59. new CodeThisReferenceExpression(),
  60. "ExecuteTask",
  61. new CodeTypeReference(methodInfo.ReturnType.GetGenericArguments()[0])
  62. ),
  63. new CodeExpression[] {
  64. new CodePrimitiveExpression(methodInfo.Name),
  65. PassParameterNames(methodInfo),
  66. PassParameterValues(methodInfo),
  67. }
  68. .ToArray()
  69. );
  70. return execution;
  71. }
  72. private static Type CreateClientImplementation(Type interfaceType)
  73. {
  74. Type clientType = null;
  75. if (!clientImplementations.TryGetValue(interfaceType, out clientType))
  76. {
  77. Type callbackType = interfaceType.FindInterfaces(
  78. (t, o) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDuplexNodeEndpointClient<>),
  79. null
  80. )
  81. .FirstOrDefault();
  82. CodeDomProvider codedomProvider = CodeDomProvider.CreateProvider("CSharp");
  83. string namespaceName = "NodeService.StringTypedNodeEndpointClientAutoGeneratedClients";
  84. string typeName = "ImplementationOf" + interfaceType.Name;
  85. CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
  86. {
  87. CodeNamespace codeNamespace = new CodeNamespace(namespaceName);
  88. codeCompileUnit.Namespaces.Add(codeNamespace);
  89. CodeTypeDeclaration clientDeclaration = new CodeTypeDeclaration(typeName);
  90. if (callbackType == null)
  91. {
  92. clientDeclaration.BaseTypes.Add(typeof(StrongTypedNodeEndpointClient));
  93. }
  94. else
  95. {
  96. clientDeclaration.BaseTypes.Add(typeof(StrongTypedNodeEndpointClient<>).MakeGenericType(callbackType.GetGenericArguments()[0]));
  97. }
  98. clientDeclaration.BaseTypes.Add(interfaceType);
  99. codeNamespace.Types.Add(clientDeclaration);
  100. CodeConstructor clientConstructor = new CodeConstructor();
  101. clientDeclaration.Members.Add(clientConstructor);
  102. clientConstructor.Attributes = MemberAttributes.Public;
  103. clientConstructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(INodeEndpointClientProvider), "provider"));
  104. clientConstructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("provider"));
  105. clientConstructor.Statements.Add(
  106. new CodeExpressionStatement(
  107. new CodeMethodInvokeExpression(
  108. new CodeThisReferenceExpression(),
  109. "Initialize",
  110. new CodeTypeOfExpression(interfaceType)
  111. )
  112. )
  113. );
  114. var methodInfos = GetMethodInfos(interfaceType);
  115. foreach (var methodInfo in methodInfos)
  116. {
  117. CodeMemberMethod clientMethod = new CodeMemberMethod();
  118. clientDeclaration.Members.Add(clientMethod);
  119. clientMethod.Attributes = MemberAttributes.Public;
  120. clientMethod.Name = methodInfo.Name;
  121. clientMethod.ImplementationTypes.Add(methodInfo.DeclaringType);
  122. clientMethod.ReturnType = new CodeTypeReference(methodInfo.ReturnType);
  123. foreach (var parameterInfo in methodInfo.GetParameters())
  124. {
  125. clientMethod.Parameters.Add(new CodeParameterDeclarationExpression(parameterInfo.ParameterType, parameterInfo.Name));
  126. }
  127. if (methodInfo.ReturnType == typeof(void))
  128. {
  129. clientMethod.Statements.Add(
  130. new CodeExpressionStatement(
  131. InvokeExecute(methodInfo)
  132. )
  133. );
  134. }
  135. else if (methodInfo.ReturnType == typeof(Task))
  136. {
  137. clientMethod.Statements.Add(
  138. new CodeMethodReturnStatement(
  139. InvokeExecuteTask(methodInfo)
  140. )
  141. );
  142. }
  143. else if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
  144. {
  145. clientMethod.Statements.Add(
  146. new CodeMethodReturnStatement(
  147. InvokeExecuteTask(methodInfo)
  148. )
  149. );
  150. }
  151. else
  152. {
  153. clientMethod.Statements.Add(
  154. new CodeMethodReturnStatement(
  155. new CodeCastExpression(
  156. methodInfo.ReturnType,
  157. InvokeExecute(methodInfo)
  158. )
  159. )
  160. );
  161. }
  162. }
  163. }
  164. List<Type> interfaceTypes = new List<Type>();
  165. CollectType(interfaceType, interfaceTypes);
  166. string[] assemblies = new string[] { typeof(StrongTypedNodeEndpointClient).Assembly.Location }
  167. .Concat(typeof(StrongTypedNodeEndpointClient).Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location))
  168. .Concat(typeof(XElement).Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location))
  169. .Concat(interfaceTypes.Select(t => t.Assembly.Location))
  170. .Concat(interfaceTypes.SelectMany(t => t.Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location)))
  171. .Where(s => s != null)
  172. .Distinct()
  173. .ToArray();
  174. CompilerParameters options = new CompilerParameters();
  175. options.GenerateExecutable = false;
  176. options.GenerateInMemory = true;
  177. options.IncludeDebugInformation = false;
  178. options.ReferencedAssemblies.AddRange(assemblies);
  179. CompilerResults result = codedomProvider.CompileAssemblyFromDom(options, codeCompileUnit);
  180. codedomProvider.Dispose();
  181. clientType = result.CompiledAssembly.GetType(namespaceName + "." + typeName);
  182. clientImplementations.Add(interfaceType, clientType);
  183. }
  184. return clientType;
  185. }
  186. private static void CollectType(Type interfaceType, List<Type> types)
  187. {
  188. if (!types.Contains(interfaceType))
  189. {
  190. types.Add(interfaceType);
  191. foreach (var type in interfaceType.GetInterfaces())
  192. {
  193. CollectType(type, types);
  194. }
  195. if (interfaceType.IsGenericType)
  196. {
  197. foreach (var type in interfaceType.GetGenericArguments())
  198. {
  199. CollectType(type, types);
  200. }
  201. }
  202. }
  203. }
  204. public static MethodInfo[] GetMethodInfos(Type interfaceType)
  205. {
  206. var methodInfos = new Type[] { interfaceType }
  207. .Concat(interfaceType.GetInterfaces())
  208. .Where(t =>
  209. t != typeof(INodeEndpointClient) &&
  210. t != typeof(IDisposable) &&
  211. (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(IDuplexNodeEndpointClient<>))
  212. )
  213. .SelectMany(t => t.GetMethods())
  214. .Where(m => !m.IsSpecialName)
  215. .ToArray();
  216. return methodInfos;
  217. }
  218. public static T Create<T>(INodeEndpointClientProvider provider)
  219. where T : INodeEndpointClient
  220. {
  221. Type clientType = CreateClientImplementation(typeof(T));
  222. return (T)clientType
  223. .GetConstructor(new Type[] { typeof(INodeEndpointClientProvider) })
  224. .Invoke(new object[] { provider });
  225. }
  226. }
  227. }