You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
552 lines
22 KiB
552 lines
22 KiB
//------------------------------------------------------------------------------ |
|
// 此代码版权(除特别声明或在XREF结尾的命名空间的代码)归作者本人若汝棋茗所有 |
|
// 源代码使用协议遵循本仓库的开源协议及附加协议,若本仓库没有设置,则按MIT开源协议授权 |
|
// CSDN博客:https://blog.csdn.net/qq_40374647 |
|
// 哔哩哔哩视频:https://space.bilibili.com/94253567 |
|
// Gitee源代码仓库:https://gitee.com/RRQM_Home |
|
// Github源代码仓库:https://github.com/RRQM |
|
// API首页:https://www.yuque.com/rrqm/touchsocket/index |
|
// 交流QQ群:234762506 |
|
// 感谢您的下载和使用 |
|
//------------------------------------------------------------------------------ |
|
//------------------------------------------------------------------------------ |
|
using System; |
|
using System.Collections.Generic; |
|
using System.Linq; |
|
using System.Reflection; |
|
using System.Text; |
|
using TouchSocket.Core; |
|
|
|
namespace TouchSocket.Rpc |
|
{ |
|
/// <summary> |
|
/// 代码生成器 |
|
/// </summary> |
|
public static class CodeGenerator |
|
{ |
|
internal static readonly List<Assembly> m_assemblies = new List<Assembly>(); |
|
internal static readonly List<Assembly> m_ignoreAssemblies = new List<Assembly>(); |
|
internal static readonly List<Type> m_ignoreTypes = new List<Type>(); |
|
internal static readonly Dictionary<Type, string> m_proxyType = new Dictionary<Type, string>(); |
|
private const BindingFlags m_methodFlags = BindingFlags.Default | BindingFlags.Instance | BindingFlags.Public; |
|
|
|
/// <summary> |
|
/// 添加不需要代理的程序集 |
|
/// </summary> |
|
/// <param name="assembly"></param> |
|
public static void AddIgnoreProxyAssembly(Assembly assembly) |
|
{ |
|
m_ignoreAssemblies.Add(assembly); |
|
} |
|
|
|
/// <summary> |
|
/// 添加不需要代理的类型 |
|
/// </summary> |
|
/// <param name="type"></param> |
|
public static void AddIgnoreProxyType(Type type) |
|
{ |
|
m_ignoreTypes.Add(type); |
|
} |
|
|
|
/// <summary> |
|
/// 添加需要代理的程序集 |
|
/// </summary> |
|
/// <param name="assembly"></param> |
|
public static void AddProxyAssembly(Assembly assembly) |
|
{ |
|
m_assemblies.Add(assembly); |
|
} |
|
|
|
/// <summary> |
|
/// 添加代理类型 |
|
/// </summary> |
|
/// <param name="type"></param> |
|
/// <param name="deepSearch"></param> |
|
public static void AddProxyType(Type type, bool deepSearch = true) |
|
{ |
|
if (type.IsPrimitive || type == typeof(string)) |
|
{ |
|
return; |
|
} |
|
if (!m_proxyType.ContainsKey(type)) |
|
{ |
|
RpcProxyAttribute attribute = type.GetCustomAttribute<RpcProxyAttribute>(); |
|
m_proxyType.Add(type, attribute == null ? type.Name : attribute.ClassName); |
|
if (deepSearch) |
|
{ |
|
PropertyInfo[] properties = type.GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.GetProperty | BindingFlags.SetProperty); |
|
foreach (var item in properties) |
|
{ |
|
AddProxyType(item.PropertyType); |
|
} |
|
} |
|
} |
|
} |
|
|
|
/// <summary> |
|
/// 添加代理类型 |
|
/// </summary> |
|
/// <typeparam name="T"></typeparam> |
|
/// <param name="deepSearch"></param> |
|
public static void AddProxyType<T>(bool deepSearch = true) |
|
{ |
|
AddProxyType(typeof(T), deepSearch); |
|
} |
|
|
|
/// <summary> |
|
/// 是否包含类型 |
|
/// </summary> |
|
/// <param name="type"></param> |
|
/// <returns></returns> |
|
public static bool ContainsType(Type type) |
|
{ |
|
return m_proxyType.ContainsKey(type); |
|
} |
|
|
|
/// <summary> |
|
/// 转换为cs代码。 |
|
/// </summary> |
|
/// <param name="namespace"></param> |
|
/// <param name="serverCodes"></param> |
|
/// <returns></returns> |
|
public static string ConvertToCode(string @namespace, params ServerCellCode[] serverCodes) |
|
{ |
|
Dictionary<string, ServerCellCode> serverCellCodes = new Dictionary<string, ServerCellCode>(); |
|
Dictionary<string, ClassCellCode> classCellCodes = new Dictionary<string, ClassCellCode>(); |
|
StringBuilder codeString = new StringBuilder(); |
|
foreach (var serverCellCode in serverCodes) |
|
{ |
|
if (serverCellCodes.ContainsKey(serverCellCode.Name)) |
|
{ |
|
if (serverCellCode.IncludeExtension) |
|
{ |
|
serverCellCodes[serverCellCode.Name].IncludeExtension = true; |
|
} |
|
|
|
if (serverCellCode.IncludeInstance) |
|
{ |
|
serverCellCodes[serverCellCode.Name].IncludeInstance = true; |
|
} |
|
|
|
if (serverCellCode.IncludeInterface) |
|
{ |
|
serverCellCodes[serverCellCode.Name].IncludeInterface = true; |
|
} |
|
|
|
var ccm = serverCellCodes[serverCellCode.Name].Methods; |
|
foreach (var item in serverCellCode.Methods.Keys) |
|
{ |
|
if (!ccm.ContainsKey(item)) |
|
{ |
|
ccm.Add(item, serverCellCode.Methods[item]); |
|
} |
|
} |
|
} |
|
else |
|
{ |
|
serverCellCodes.Add(serverCellCode.Name, serverCellCode); |
|
} |
|
|
|
foreach (var item in serverCellCode.ClassCellCodes.Keys) |
|
{ |
|
if (!classCellCodes.ContainsKey(item)) |
|
{ |
|
classCellCodes.Add(item, serverCellCode.ClassCellCodes[item]); |
|
} |
|
} |
|
} |
|
|
|
string namesp = string.IsNullOrEmpty(@namespace) ? "RRQMProxy" : @namespace; |
|
|
|
codeString.AppendLine("using System;"); |
|
codeString.AppendLine("using TouchSocket.Core;"); |
|
codeString.AppendLine("using TouchSocket.Sockets;"); |
|
codeString.AppendLine("using TouchSocket.Rpc;"); |
|
codeString.AppendLine("using TouchSocket.Rpc.TouchRpc;"); |
|
codeString.AppendLine("using System.Collections.Generic;"); |
|
codeString.AppendLine("using System.Diagnostics;"); |
|
codeString.AppendLine("using System.Text;"); |
|
codeString.AppendLine("using System.Threading.Tasks;"); |
|
codeString.AppendLine(string.Format("namespace {0}", namesp)); |
|
codeString.AppendLine("{"); |
|
|
|
foreach (var serverCellCode in serverCellCodes.Values) |
|
{ |
|
if (serverCellCode.IncludeInterface) |
|
{ |
|
//接口 |
|
codeString.AppendLine($"public interface I{serverCellCode.Name}:{typeof(IRemoteServer).FullName}");//类开始 |
|
codeString.AppendLine("{"); |
|
foreach (var item in serverCellCode.Methods.Values) |
|
{ |
|
codeString.AppendLine(item.InterfaceTemple); |
|
} |
|
codeString.AppendLine("}"); |
|
//接口 |
|
} |
|
|
|
if (serverCellCode.IncludeInstance) |
|
{ |
|
//类 |
|
if (serverCellCode.IncludeInterface) |
|
{ |
|
codeString.AppendLine($"public class {serverCellCode.Name} :I{serverCellCode.Name}");//类开始 |
|
} |
|
else |
|
{ |
|
codeString.AppendLine($"public class {serverCellCode.Name}");//类开始 |
|
} |
|
|
|
codeString.AppendLine("{"); |
|
codeString.AppendLine($"public {serverCellCode.Name}(IRpcClient client)"); |
|
codeString.AppendLine("{"); |
|
codeString.AppendLine("this.Client=client;"); |
|
codeString.AppendLine("}"); |
|
codeString.AppendLine("public IRpcClient Client{get;private set; }"); |
|
foreach (var item in serverCellCode.Methods.Values) |
|
{ |
|
codeString.AppendLine(item.CodeTemple); |
|
} |
|
codeString.AppendLine("}"); |
|
//类 |
|
} |
|
|
|
if (serverCellCode.IncludeExtension) |
|
{ |
|
//扩展类 |
|
codeString.AppendLine($"public static class {serverCellCode.Name}Extensions");//类开始 |
|
codeString.AppendLine("{"); |
|
foreach (var item in serverCellCode.Methods.Values) |
|
{ |
|
codeString.AppendLine(item.ExtensionsTemple); |
|
} |
|
codeString.AppendLine("}"); |
|
//扩展类 |
|
} |
|
} |
|
|
|
foreach (var item in classCellCodes.Values) |
|
{ |
|
codeString.AppendLine(item.Code); |
|
} |
|
|
|
codeString.AppendLine("}"); |
|
|
|
return codeString.ToString(); |
|
} |
|
|
|
/// <summary> |
|
/// 生成代码代理 |
|
/// </summary> |
|
/// <typeparam name="TServer">服务类型</typeparam> |
|
/// <typeparam name="TAttribute">属性标签</typeparam> |
|
/// <returns></returns> |
|
public static ServerCellCode Generator<TServer, TAttribute>() where TServer : IRpcServer where TAttribute : RpcAttribute |
|
{ |
|
return Generator(typeof(TServer), typeof(TAttribute)); |
|
} |
|
|
|
/// <summary> |
|
/// 生成代码代理 |
|
/// </summary> |
|
/// <param name="serverType">服务类型</param> |
|
/// <param name="attributeType"></param> |
|
/// <returns></returns> |
|
public static ServerCellCode Generator(Type serverType, Type attributeType) |
|
{ |
|
ServerCellCode serverCellCode = new ServerCellCode(); |
|
MethodInstance[] methodInstances = GetMethodInstances(serverType, serverType); |
|
|
|
List<Assembly> assemblies = new List<Assembly>(m_assemblies); |
|
assemblies.Add(serverType.Assembly); |
|
|
|
foreach (var item in m_ignoreAssemblies) |
|
{ |
|
assemblies.Remove(item); |
|
} |
|
|
|
ClassCodeGenerator classCodeGenerator = new ClassCodeGenerator(assemblies.ToArray()); |
|
|
|
serverCellCode.Name = serverType.IsInterface ? |
|
(serverType.Name.StartsWith("I") ? serverType.Name.Remove(0, 1) : serverType.Name) : serverType.Name; |
|
List<MethodInstance> instances = new List<MethodInstance>(); |
|
|
|
foreach (var item in m_proxyType.Keys) |
|
{ |
|
int deep = 0; |
|
classCodeGenerator.AddTypeString(item, ref deep); |
|
} |
|
|
|
foreach (MethodInstance methodInstance in methodInstances) |
|
{ |
|
foreach (RpcAttribute att in methodInstance.RpcAttributes) |
|
{ |
|
if (attributeType == att.GetType()) |
|
{ |
|
if (methodInstance.ReturnType != null) |
|
{ |
|
int deep = 0; |
|
classCodeGenerator.AddTypeString(methodInstance.ReturnType, ref deep); |
|
} |
|
|
|
int i = 0; |
|
if (methodInstance.MethodFlags.HasFlag(MethodFlags.IncludeCallContext)) |
|
{ |
|
i = 1; |
|
} |
|
for (; i < methodInstance.ParameterTypes.Length; i++) |
|
{ |
|
int deep = 0; |
|
classCodeGenerator.AddTypeString(methodInstance.ParameterTypes[i], ref deep); |
|
} |
|
|
|
instances.Add(methodInstance); |
|
break; |
|
} |
|
} |
|
} |
|
|
|
classCodeGenerator.CheckDeep(); |
|
|
|
//foreach (var item in classCodeGenerator.GenericTypeDic.Keys.ToArray()) |
|
//{ |
|
// if (m_ignoreTypes.Contains(item)) |
|
// { |
|
// classCodeGenerator.GenericTypeDic.TryRemove(item, out _); |
|
// } |
|
|
|
// if (m_ignoreAssemblies.Contains(item.Assembly)) |
|
// { |
|
// classCodeGenerator.GenericTypeDic.TryRemove(item, out _); |
|
// } |
|
//} |
|
|
|
foreach (var item in classCodeGenerator.PropertyDic.Keys.ToArray()) |
|
{ |
|
if (m_ignoreTypes.Contains(item)) |
|
{ |
|
classCodeGenerator.PropertyDic.TryRemove(item, out _); |
|
} |
|
|
|
if (m_ignoreAssemblies.Contains(item.Assembly)) |
|
{ |
|
classCodeGenerator.PropertyDic.TryRemove(item, out _); |
|
} |
|
} |
|
|
|
foreach (var item in classCodeGenerator.GetClassCellCodes()) |
|
{ |
|
serverCellCode.ClassCellCodes.Add(item.Name, item); |
|
} |
|
|
|
//ServerCodeGenerator serverCodeGenerator = new ServerCodeGenerator(classCodeGenerator); |
|
|
|
bool first = true; |
|
foreach (var item in instances) |
|
{ |
|
MethodCellCode methodCellCode = new MethodCellCode(); |
|
RpcAttribute rpcAttribute = (RpcAttribute)item.GetAttribute(attributeType); |
|
if (rpcAttribute == null) |
|
{ |
|
continue; |
|
} |
|
rpcAttribute.SetClassCodeGenerator(classCodeGenerator); |
|
if (first) |
|
{ |
|
if (rpcAttribute.GeneratorFlag.HasFlag(CodeGeneratorFlag.InterfaceAsync) || rpcAttribute.GeneratorFlag.HasFlag(CodeGeneratorFlag.InterfaceSync)) |
|
{ |
|
serverCellCode.IncludeInterface = true; |
|
} |
|
if (rpcAttribute.GeneratorFlag.HasFlag(CodeGeneratorFlag.InstanceAsync) || rpcAttribute.GeneratorFlag.HasFlag(CodeGeneratorFlag.InstanceSync)) |
|
{ |
|
serverCellCode.IncludeInstance = true; |
|
} |
|
if (rpcAttribute.GeneratorFlag.HasFlag(CodeGeneratorFlag.ExtensionAsync) || rpcAttribute.GeneratorFlag.HasFlag(CodeGeneratorFlag.ExtensionSync)) |
|
{ |
|
serverCellCode.IncludeExtension = true; |
|
} |
|
first = false; |
|
} |
|
|
|
methodCellCode.InterfaceTemple = rpcAttribute.GetInterfaceProxyCode(item); |
|
methodCellCode.CodeTemple = rpcAttribute.GetInstanceProxyCode(item); |
|
methodCellCode.ExtensionsTemple = rpcAttribute.GetExtensionsMethodProxyCode(item); |
|
methodCellCode.Name = ((RpcAttribute)item.GetAttribute(attributeType)).GetMethodName(item, false); |
|
serverCellCode.Methods.Add(methodCellCode.Name, methodCellCode); |
|
} |
|
|
|
return serverCellCode; |
|
} |
|
|
|
/// <summary> |
|
/// 从类型获取函数实例 |
|
/// </summary> |
|
/// <typeparam name="TServer"></typeparam> |
|
/// <returns></returns> |
|
public static MethodInstance[] GetMethodInstances<TServer>() where TServer : IRpcServer |
|
{ |
|
return GetMethodInstances(typeof(TServer), typeof(TServer)); |
|
} |
|
|
|
/// <summary> |
|
/// 从类型获取函数实例 |
|
/// </summary> |
|
/// <param name="serverFromType"></param> |
|
/// <param name="serverToType"></param> |
|
/// <returns></returns> |
|
public static MethodInstance[] GetMethodInstances(Type serverFromType, Type serverToType) |
|
{ |
|
if (!typeof(IRpcServer).IsAssignableFrom(serverFromType)) |
|
{ |
|
throw new RpcException($"服务类型必须从{nameof(IRpcServer)}派生。"); |
|
} |
|
|
|
if (!serverFromType.IsAssignableFrom(serverToType)) |
|
{ |
|
throw new RpcException($"{serverToType}类型必须从{serverFromType}派生。"); |
|
} |
|
|
|
List<MethodInstance> instances = new List<MethodInstance>(); |
|
|
|
var fromMethodInfos = new Dictionary<string, MethodInfo>(); |
|
GetMethodInfos(serverFromType, ref fromMethodInfos); |
|
|
|
var toMethodInfos = new Dictionary<string, MethodInfo>(); |
|
GetMethodInfos(serverToType, ref toMethodInfos); |
|
|
|
foreach (MethodInfo method in fromMethodInfos.Values) |
|
{ |
|
if (method.IsGenericMethod) |
|
{ |
|
continue; |
|
} |
|
IEnumerable<RpcAttribute> attributes = method.GetCustomAttributes<RpcAttribute>(true); |
|
if (attributes.Count() > 0) |
|
{ |
|
MethodInstance methodInstance = new MethodInstance(method) |
|
{ |
|
ServerType = serverFromType, |
|
IsEnable = true, |
|
Parameters = method.GetParameters() |
|
}; |
|
if (!toMethodInfos.TryGetValue(GetMethodID(method), out var toMethod)) |
|
{ |
|
throw new InvalidOperationException($"没有找到方法{method.Name}的实现"); |
|
} |
|
|
|
List<IRpcActionFilter> actionFilters = new List<IRpcActionFilter>(); |
|
foreach (var item in method.GetCustomAttributes(true)) |
|
{ |
|
if (item is IRpcActionFilter filter) |
|
{ |
|
actionFilters.Add(filter); |
|
} |
|
} |
|
if (serverFromType!=serverToType) |
|
{ |
|
foreach (var item in toMethod.GetCustomAttributes(true)) |
|
{ |
|
if (item is IRpcActionFilter filter) |
|
{ |
|
actionFilters.Add(filter); |
|
} |
|
} |
|
} |
|
|
|
if (actionFilters.Count > 0) |
|
{ |
|
methodInstance.Filters = actionFilters.ToArray(); |
|
} |
|
foreach (var item in attributes) |
|
{ |
|
methodInstance.MethodFlags |= item.MethodFlags; |
|
} |
|
if (methodInstance.MethodFlags.HasFlag(MethodFlags.IncludeCallContext)) |
|
{ |
|
if (methodInstance.Parameters.Length == 0 || !typeof(ICallContext).IsAssignableFrom(methodInstance.Parameters[0].ParameterType)) |
|
{ |
|
throw new RpcException($"函数:{method},标识包含{MethodFlags.IncludeCallContext}时,必须包含{nameof(ICallContext)}或其派生类参数,且为第一参数。"); |
|
} |
|
} |
|
List<string> names = new List<string>(); |
|
foreach (var parameterInfo in methodInstance.Parameters) |
|
{ |
|
names.Add(parameterInfo.Name); |
|
} |
|
methodInstance.ParameterNames = names.ToArray(); |
|
ParameterInfo[] parameters = method.GetParameters(); |
|
List<Type> types = new List<Type>(); |
|
foreach (var parameter in parameters) |
|
{ |
|
types.Add(parameter.ParameterType.GetRefOutType()); |
|
} |
|
methodInstance.ParameterTypes = types.ToArray(); |
|
instances.Add(methodInstance); |
|
} |
|
} |
|
|
|
return instances.ToArray(); |
|
} |
|
|
|
/// <summary> |
|
/// 生成代理代码 |
|
/// </summary> |
|
/// <param name="namespace"></param> |
|
/// <param name="serverTypes"></param> |
|
/// <param name="attributeTypes"></param> |
|
/// <returns></returns> |
|
public static string GetProxyCodes(string @namespace, Type[] serverTypes, Type[] attributeTypes) |
|
{ |
|
List<ServerCellCode> serverCellCodeList = new List<ServerCellCode>(); |
|
foreach (var item in serverTypes) |
|
{ |
|
foreach (var item1 in attributeTypes) |
|
{ |
|
serverCellCodeList.Add(Generator(item, item1)); |
|
} |
|
} |
|
return ConvertToCode(@namespace, serverCellCodeList.ToArray()); |
|
} |
|
|
|
/// <summary> |
|
/// 获取类型代理名称 |
|
/// </summary> |
|
/// <param name="type"></param> |
|
/// <param name="className"></param> |
|
/// <returns></returns> |
|
public static bool TryGetProxyTypeName(Type type, out string className) |
|
{ |
|
return m_proxyType.TryGetValue(type, out className); |
|
} |
|
|
|
private static string GetMethodID(MethodInfo method) |
|
{ |
|
StringBuilder stringBuilder = new StringBuilder(); |
|
stringBuilder.Append(method.Name); |
|
foreach (var item in method.GetParameters()) |
|
{ |
|
stringBuilder.Append(item.ParameterType.FullName); |
|
} |
|
|
|
return stringBuilder.ToString(); |
|
} |
|
|
|
private static void GetMethodInfos(Type type, ref Dictionary<string, MethodInfo> infos) |
|
{ |
|
if (type.IsInterface) |
|
{ |
|
foreach (var item in type.GetInterfaces()) |
|
{ |
|
GetMethodInfos(item, ref infos); |
|
} |
|
} |
|
foreach (var item in type.GetMethods(m_methodFlags)) |
|
{ |
|
if (!infos.ContainsKey(GetMethodID(item))) |
|
{ |
|
infos.Add(GetMethodID(item), item); |
|
} |
|
} |
|
} |
|
} |
|
} |