/Candidate/NodeService/NodeService/Endpoints/StrongTypedNodeEndpointClientBuilder.cs
C# | 248 lines | 227 code | 21 blank | 0 comment | 22 complexity | b8032742a0ac5fbd5ea38e464f0404ef MD5 | raw file
- using System;
- using System.Collections.Generic;
- using System.Linq;
- using System.Text;
- using System.CodeDom;
- using System.Reflection;
- using System.CodeDom.Compiler;
- using System.Threading.Tasks;
- using System.Xml.Linq;
-
- namespace NodeService.Endpoints
- {
- public static class StrongTypedNodeEndpointClientBuilder
- {
- private static Dictionary<Type, Type> clientImplementations = new Dictionary<Type, Type>();
-
- private static CodeExpression PassParameterNames(MethodInfo methodInfo)
- {
- return new CodeArrayCreateExpression(
- typeof(string),
- methodInfo
- .GetParameters()
- .Select(p => new CodePrimitiveExpression(p.Name))
- .ToArray()
- );
- }
-
- private static CodeExpression PassParameterValues(MethodInfo methodInfo)
- {
- return new CodeArrayCreateExpression(
- typeof(object),
- methodInfo
- .GetParameters()
- .Select(p => new CodeArgumentReferenceExpression(p.Name))
- .ToArray()
- );
- }
-
- private static CodeExpression InvokeExecute(MethodInfo methodInfo)
- {
- CodeExpression execution = new CodeMethodInvokeExpression(
- new CodeThisReferenceExpression(),
- "Execute",
- new CodeExpression[] {
- new CodePrimitiveExpression(methodInfo.Name),
- new CodeTypeOfExpression(methodInfo.ReturnType),
- PassParameterNames(methodInfo),
- PassParameterValues(methodInfo),
- }
- .ToArray()
- );
- return execution;
- }
-
- private static CodeExpression InvokeExecuteTask(MethodInfo methodInfo)
- {
- CodeExpression execution = new CodeMethodInvokeExpression(
- methodInfo.ReturnType == typeof(Task) ?
- new CodeMethodReferenceExpression(
- new CodeThisReferenceExpression(),
- "ExecuteTask"
- ) :
- new CodeMethodReferenceExpression(
- new CodeThisReferenceExpression(),
- "ExecuteTask",
- new CodeTypeReference(methodInfo.ReturnType.GetGenericArguments()[0])
- ),
- new CodeExpression[] {
- new CodePrimitiveExpression(methodInfo.Name),
- PassParameterNames(methodInfo),
- PassParameterValues(methodInfo),
- }
- .ToArray()
- );
- return execution;
- }
-
- private static Type CreateClientImplementation(Type interfaceType)
- {
- Type clientType = null;
- if (!clientImplementations.TryGetValue(interfaceType, out clientType))
- {
- Type callbackType = interfaceType.FindInterfaces(
- (t, o) => t.IsGenericType && t.GetGenericTypeDefinition() == typeof(IDuplexNodeEndpointClient<>),
- null
- )
- .FirstOrDefault();
-
- CodeDomProvider codedomProvider = CodeDomProvider.CreateProvider("CSharp");
- string namespaceName = "NodeService.StringTypedNodeEndpointClientAutoGeneratedClients";
- string typeName = "ImplementationOf" + interfaceType.Name;
-
- CodeCompileUnit codeCompileUnit = new CodeCompileUnit();
- {
- CodeNamespace codeNamespace = new CodeNamespace(namespaceName);
- codeCompileUnit.Namespaces.Add(codeNamespace);
-
- CodeTypeDeclaration clientDeclaration = new CodeTypeDeclaration(typeName);
- if (callbackType == null)
- {
- clientDeclaration.BaseTypes.Add(typeof(StrongTypedNodeEndpointClient));
- }
- else
- {
- clientDeclaration.BaseTypes.Add(typeof(StrongTypedNodeEndpointClient<>).MakeGenericType(callbackType.GetGenericArguments()[0]));
- }
- clientDeclaration.BaseTypes.Add(interfaceType);
- codeNamespace.Types.Add(clientDeclaration);
-
- CodeConstructor clientConstructor = new CodeConstructor();
- clientDeclaration.Members.Add(clientConstructor);
- clientConstructor.Attributes = MemberAttributes.Public;
- clientConstructor.Parameters.Add(new CodeParameterDeclarationExpression(typeof(INodeEndpointClientProvider), "provider"));
- clientConstructor.BaseConstructorArgs.Add(new CodeArgumentReferenceExpression("provider"));
- clientConstructor.Statements.Add(
- new CodeExpressionStatement(
- new CodeMethodInvokeExpression(
- new CodeThisReferenceExpression(),
- "Initialize",
- new CodeTypeOfExpression(interfaceType)
- )
- )
- );
-
- var methodInfos = GetMethodInfos(interfaceType);
-
- foreach (var methodInfo in methodInfos)
- {
- CodeMemberMethod clientMethod = new CodeMemberMethod();
- clientDeclaration.Members.Add(clientMethod);
- clientMethod.Attributes = MemberAttributes.Public;
- clientMethod.Name = methodInfo.Name;
- clientMethod.ImplementationTypes.Add(methodInfo.DeclaringType);
- clientMethod.ReturnType = new CodeTypeReference(methodInfo.ReturnType);
- foreach (var parameterInfo in methodInfo.GetParameters())
- {
- clientMethod.Parameters.Add(new CodeParameterDeclarationExpression(parameterInfo.ParameterType, parameterInfo.Name));
- }
-
-
- if (methodInfo.ReturnType == typeof(void))
- {
- clientMethod.Statements.Add(
- new CodeExpressionStatement(
- InvokeExecute(methodInfo)
- )
- );
- }
- else if (methodInfo.ReturnType == typeof(Task))
- {
- clientMethod.Statements.Add(
- new CodeMethodReturnStatement(
- InvokeExecuteTask(methodInfo)
- )
- );
- }
- else if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
- {
- clientMethod.Statements.Add(
- new CodeMethodReturnStatement(
- InvokeExecuteTask(methodInfo)
- )
- );
- }
- else
- {
- clientMethod.Statements.Add(
- new CodeMethodReturnStatement(
- new CodeCastExpression(
- methodInfo.ReturnType,
- InvokeExecute(methodInfo)
- )
- )
- );
- }
- }
- }
-
- List<Type> interfaceTypes = new List<Type>();
- CollectType(interfaceType, interfaceTypes);
-
- string[] assemblies = new string[] { typeof(StrongTypedNodeEndpointClient).Assembly.Location }
- .Concat(typeof(StrongTypedNodeEndpointClient).Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location))
- .Concat(typeof(XElement).Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location))
- .Concat(interfaceTypes.Select(t => t.Assembly.Location))
- .Concat(interfaceTypes.SelectMany(t => t.Assembly.GetReferencedAssemblies().Select(n => Assembly.Load(n).Location)))
- .Where(s => s != null)
- .Distinct()
- .ToArray();
-
- CompilerParameters options = new CompilerParameters();
- options.GenerateExecutable = false;
- options.GenerateInMemory = true;
- options.IncludeDebugInformation = false;
- options.ReferencedAssemblies.AddRange(assemblies);
-
- CompilerResults result = codedomProvider.CompileAssemblyFromDom(options, codeCompileUnit);
- codedomProvider.Dispose();
- clientType = result.CompiledAssembly.GetType(namespaceName + "." + typeName);
- clientImplementations.Add(interfaceType, clientType);
- }
- return clientType;
- }
-
- private static void CollectType(Type interfaceType, List<Type> types)
- {
- if (!types.Contains(interfaceType))
- {
- types.Add(interfaceType);
- foreach (var type in interfaceType.GetInterfaces())
- {
- CollectType(type, types);
- }
- if (interfaceType.IsGenericType)
- {
- foreach (var type in interfaceType.GetGenericArguments())
- {
- CollectType(type, types);
- }
- }
- }
- }
-
- public static MethodInfo[] GetMethodInfos(Type interfaceType)
- {
- var methodInfos = new Type[] { interfaceType }
- .Concat(interfaceType.GetInterfaces())
- .Where(t =>
- t != typeof(INodeEndpointClient) &&
- t != typeof(IDisposable) &&
- (!t.IsGenericType || t.GetGenericTypeDefinition() != typeof(IDuplexNodeEndpointClient<>))
- )
- .SelectMany(t => t.GetMethods())
- .Where(m => !m.IsSpecialName)
- .ToArray();
- return methodInfos;
- }
-
- public static T Create<T>(INodeEndpointClientProvider provider)
- where T : INodeEndpointClient
- {
- Type clientType = CreateClientImplementation(typeof(T));
- return (T)clientType
- .GetConstructor(new Type[] { typeof(INodeEndpointClientProvider) })
- .Invoke(new object[] { provider });
- }
- }
- }