2024-11-03 21:17:45 +08:00
|
|
|
|
using Serein.Library;
|
2024-11-08 17:30:51 +08:00
|
|
|
|
using Serein.Library.Utils;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
using Serein.NodeFlow.Tool;
|
|
|
|
|
|
using System;
|
|
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
|
using System.Collections.Generic;
|
|
|
|
|
|
using System.Diagnostics;
|
2025-03-15 15:43:42 +08:00
|
|
|
|
using System.Globalization;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
using System.IO;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Reflection;
|
|
|
|
|
|
using System.Text;
|
2025-03-15 15:43:42 +08:00
|
|
|
|
using static System.Runtime.InteropServices.JavaScript.JSType;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
namespace Serein.NodeFlow.Model.Library
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-03-15 15:43:42 +08:00
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2024-11-04 23:30:52 +08:00
|
|
|
|
/// 加载在流程中的程序集依赖
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// </summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public class FlowLibraryCache
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 通过程序集创建一个流程库实例
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="assembly"></param>
|
|
|
|
|
|
public FlowLibraryCache(Assembly assembly)
|
2025-03-18 11:52:54 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
Assembly = assembly;
|
|
|
|
|
|
FullName = Path.GetFileName(Assembly.Location);
|
|
|
|
|
|
FilePath = Assembly.Location;
|
2025-03-18 11:52:54 +08:00
|
|
|
|
}
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 通过动态程序集和文件路径创建一个流程库实例
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="dynamicAssembly"></param>
|
|
|
|
|
|
/// <param name="filePath"></param>
|
|
|
|
|
|
public FlowLibraryCache(Assembly dynamicAssembly,
|
2025-03-18 11:52:54 +08:00
|
|
|
|
string filePath)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
Assembly = dynamicAssembly;
|
|
|
|
|
|
FullName = Path.GetFileName(filePath); ;
|
|
|
|
|
|
FilePath = filePath;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 程序集本身
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Assembly Assembly { get; private set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 程序集全名
|
|
|
|
|
|
/// </summary>
|
2025-03-18 11:52:54 +08:00
|
|
|
|
public string FullName { get; private set; }
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 程序集文件路径
|
|
|
|
|
|
/// </summary>
|
2025-03-18 11:52:54 +08:00
|
|
|
|
public string FilePath { get; private set; }
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载程序集时创建的方法描述
|
2024-11-04 23:30:52 +08:00
|
|
|
|
/// Key : 方法名称
|
|
|
|
|
|
/// Value :方法详情
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// </summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public Dictionary<string, MethodDetails> MethodDetailss { get; } = new Dictionary<string, MethodDetails>();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// 加载程序集时创建的方法信息
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// </summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public Dictionary<string, MethodInfo> MethodInfos { get; } = new Dictionary<string, MethodInfo>();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <para>缓存节点方法通Emit委托</para>
|
|
|
|
|
|
/// <para>Key :方法名称</para>
|
|
|
|
|
|
/// <para>Value :方法详情</para>
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// </summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public Dictionary<string, DelegateDetails> DelegateDetailss { get; } = new Dictionary<string, DelegateDetails>();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// 用于流程启动时,在不同阶段(Init_Loading_Loaded)需要创建实例的类型信息
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// </summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public Dictionary<RegisterSequence, List<Type>> RegisterTypes { get; } = new Dictionary<RegisterSequence, List<Type>>();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 动态加载程序集
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public bool LoadFlowMethod()
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
Assembly assembly = Assembly;
|
2025-07-07 20:40:24 +08:00
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
#region 检查入参
|
|
|
|
|
|
|
|
|
|
|
|
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
|
|
|
|
|
var assemblyName = assembly.GetName().Name;
|
|
|
|
|
|
if (string.IsNullOrEmpty(assemblyName)) // 防止动态程序集没有定义程序集名称
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-11-03 23:51:18 +08:00
|
|
|
|
List<Type> types;
|
|
|
|
|
|
try
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-03 23:51:18 +08:00
|
|
|
|
types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型
|
2025-07-28 12:16:29 +08:00
|
|
|
|
if (types.Count <= 0)
|
2024-11-03 23:51:18 +08:00
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (ReflectionTypeLoadException ex)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 获取加载失败的类型
|
|
|
|
|
|
var loaderExceptions = ex.LoaderExceptions;
|
|
|
|
|
|
foreach (var loaderException in loaderExceptions)
|
|
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
SereinEnv.WriteLine(InfoType.ERROR, "加载失败 : " + loaderException?.Message);
|
2024-11-03 23:51:18 +08:00
|
|
|
|
}
|
2024-11-03 21:17:45 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#endregion
|
2025-07-07 20:40:24 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#region 获取 DynamicFlow 特性的流程控制器,如果没有退出
|
|
|
|
|
|
// Type : 具有 DynamicFlowAttribute 标记的类型
|
|
|
|
|
|
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性 (用于生成方法描述时,添加在方法别名中提高可读性)
|
|
|
|
|
|
List<(Type Type, string Name)> scanTypes = new List<(Type Type, string Name)>();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
// (Type, string)
|
|
|
|
|
|
// Type : 具有 DynamicFlowAttribute 标记的类型
|
|
|
|
|
|
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性
|
2024-11-04 23:30:52 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
types = types.Where(type => type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute df && df.Scan).ToList();
|
2024-11-04 23:30:52 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
foreach (var type in types)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-08 17:30:51 +08:00
|
|
|
|
scanTypes.Add((type, dynamicFlowAttribute.Name));
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
2024-11-08 17:30:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
if (scanTypes.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
// 类型没有流程控制器
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#region 创建对应的方法元数据
|
|
|
|
|
|
// 从 scanTypes.Type 创建的方法信息
|
|
|
|
|
|
// Md : 方法描述
|
|
|
|
|
|
// Dd :方法对应的Emit委托
|
2025-03-15 15:43:42 +08:00
|
|
|
|
List<LibraryMdDd> detailss = new List<LibraryMdDd>();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
// 遍历扫描的类型
|
|
|
|
|
|
foreach ((var type, var flowName) in scanTypes)
|
|
|
|
|
|
{
|
|
|
|
|
|
var methodInfos = NodeMethodDetailsHelper.GetMethodsToProcess(type);
|
|
|
|
|
|
foreach (var methodInfo in methodInfos) // 遍历流程控制器类型中的方法信息
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-08 17:30:51 +08:00
|
|
|
|
// 尝试创建
|
|
|
|
|
|
if (!NodeMethodDetailsHelper.TryCreateDetails(type, methodInfo, assemblyName,
|
2025-07-07 20:40:24 +08:00
|
|
|
|
out var mi, out var md, out var dd)) // 返回的描述
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-08 17:30:51 +08:00
|
|
|
|
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载方法信息:{assemblyName}-{type}-{methodInfo}");
|
|
|
|
|
|
continue;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
2024-11-08 17:30:51 +08:00
|
|
|
|
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
|
2025-07-07 20:40:24 +08:00
|
|
|
|
detailss.Add(new LibraryMdDd(mi, md, dd));
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
2024-11-08 17:30:51 +08:00
|
|
|
|
}
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#endregion
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#region 检查是否成功加载,如果成功,则真正写入到缓存的集合中
|
|
|
|
|
|
if (detailss.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-03-15 15:43:42 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
// 简单排序一下
|
2025-03-15 15:43:42 +08:00
|
|
|
|
detailss.Sort((a, b) => string.Compare(a.MethodDetails.MethodName, b.MethodDetails.MethodName, StringComparison.OrdinalIgnoreCase));
|
2025-07-28 12:16:29 +08:00
|
|
|
|
|
2025-03-15 15:43:42 +08:00
|
|
|
|
foreach (var item in detailss)
|
|
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
SereinEnv.WriteLine(InfoType.INFO, "加载方法 : " + item.MethodDetails.MethodName);
|
2025-03-15 15:43:42 +08:00
|
|
|
|
|
|
|
|
|
|
}
|
2025-07-28 12:16:29 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#region 加载成功,缓存所有方法、委托的信息
|
2025-03-15 15:43:42 +08:00
|
|
|
|
foreach (var item in detailss)
|
2024-11-08 17:30:51 +08:00
|
|
|
|
{
|
2025-03-15 15:43:42 +08:00
|
|
|
|
var key = item.MethodDetails.MethodName;
|
|
|
|
|
|
MethodDetailss.TryAdd(key, item.MethodDetails);
|
2025-07-07 20:40:24 +08:00
|
|
|
|
MethodInfos.TryAdd(key, item.MethodInfo);
|
2025-03-15 15:43:42 +08:00
|
|
|
|
DelegateDetailss.TryAdd(key, item.DelegateDetails);
|
2024-11-08 17:30:51 +08:00
|
|
|
|
}
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
#region 加载成功,开始获取并记录所有需要自动实例化的类型(在流程启动时)
|
|
|
|
|
|
foreach (Type type in types)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (type.GetCustomAttribute<AutoRegisterAttribute>() is AutoRegisterAttribute attribute)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-08 17:30:51 +08:00
|
|
|
|
if (!RegisterTypes.TryGetValue(attribute.Class, out var valus))
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-08 17:30:51 +08:00
|
|
|
|
valus = new List<Type>();
|
|
|
|
|
|
RegisterTypes.TryAdd(attribute.Class, valus);
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
2024-11-08 17:30:51 +08:00
|
|
|
|
valus.Add(type);
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
2024-11-08 17:30:51 +08:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
#endregion
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-08 17:30:51 +08:00
|
|
|
|
return true;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 卸载当前程序集以及附带的所有信息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public void Unload()
|
|
|
|
|
|
{
|
|
|
|
|
|
DelegateDetailss.Clear();
|
|
|
|
|
|
MethodInfos.Clear();
|
|
|
|
|
|
RegisterTypes.Clear();
|
|
|
|
|
|
MethodDetailss.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 转为依赖信息
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public FlowLibraryInfo ToInfo()
|
|
|
|
|
|
{
|
|
|
|
|
|
var assemblyName = Assembly.GetName().Name;
|
|
|
|
|
|
var mdInfos = MethodDetailss.Values.Select(x => x.ToInfo()).ToList();
|
|
|
|
|
|
mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase));
|
|
|
|
|
|
return new FlowLibraryInfo
|
|
|
|
|
|
{
|
|
|
|
|
|
AssemblyName = assemblyName,
|
|
|
|
|
|
FileName = FullName,
|
|
|
|
|
|
FilePath = FilePath,
|
|
|
|
|
|
MethodInfos = mdInfos.ToList(),
|
|
|
|
|
|
};
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|