2024-11-03 21:17:45 +08:00
|
|
|
|
using Serein.Library;
|
|
|
|
|
|
using Serein.Library.Api;
|
2025-07-28 12:16:29 +08:00
|
|
|
|
using Serein.NodeFlow.Model.Library;
|
|
|
|
|
|
using Serein.NodeFlow.Tool;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
using System.Collections.Concurrent;
|
|
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
|
using System.Reflection;
|
2024-11-03 23:51:18 +08:00
|
|
|
|
using System.Xml.Linq;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2025-07-07 20:40:24 +08:00
|
|
|
|
namespace Serein.NodeFlow.Services
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 管理加载在运行环境中的外部程序集
|
|
|
|
|
|
/// </summary>
|
2025-07-07 20:40:24 +08:00
|
|
|
|
public class FlowLibraryService
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-04 15:46:29 +08:00
|
|
|
|
public FlowLibraryService(IFlowEnvironment flowEnvironment)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
this.flowEnvironment = flowEnvironment;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private readonly IFlowEnvironment flowEnvironment;
|
|
|
|
|
|
|
|
|
|
|
|
/// <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
|
|
|
|
private readonly ConcurrentDictionary<string, FlowLibraryCache> _flowLibraryCaches = new ConcurrentDictionary<string, FlowLibraryCache>();
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private readonly ConcurrentDictionary<string, FlowLibraryAssemblyContext> _flowLibraryAssemblyContexts
|
|
|
|
|
|
= new ConcurrentDictionary<string, FlowLibraryAssemblyContext>();
|
|
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-04 23:30:52 +08:00
|
|
|
|
/// <summary>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
|
2024-11-04 23:30:52 +08:00
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="libraryfilePath"></param>
|
|
|
|
|
|
/// <returns></returns>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
private bool CheckBaseLibrary(string libraryfilePath, out string baseLibraryPath)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-03-18 11:52:54 +08:00
|
|
|
|
var dir = Path.GetDirectoryName(libraryfilePath); // 获取目录路径
|
2025-07-28 12:16:29 +08:00
|
|
|
|
var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);
|
2025-03-18 11:52:54 +08:00
|
|
|
|
if (!Path.Exists(sereinFlowBaseLibraryPath))
|
|
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
baseLibraryPath = string.Empty;
|
|
|
|
|
|
return false;
|
2025-03-18 11:52:54 +08:00
|
|
|
|
}
|
2025-07-28 12:16:29 +08:00
|
|
|
|
baseLibraryPath = sereinFlowBaseLibraryPath;
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
2025-03-18 11:52:54 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载基础依赖
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public FlowLibraryInfo LoadBaseLibrary()
|
|
|
|
|
|
{
|
|
|
|
|
|
Assembly baseAssmbly = typeof(FlowBaseLibrary).Assembly;
|
|
|
|
|
|
var flowLibrary = new FlowLibraryCache(baseAssmbly);
|
|
|
|
|
|
flowLibrary.LoadFlowMethod();
|
|
|
|
|
|
var assemblyName = baseAssmbly.GetName().Name;
|
|
|
|
|
|
if (string.IsNullOrEmpty(assemblyName))
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"程序集\"{baseAssmbly}\"返回 Name 为 null");
|
|
|
|
|
|
}
|
|
|
|
|
|
_flowLibraryCaches.TryAdd(assemblyName, flowLibrary);
|
|
|
|
|
|
return flowLibrary.ToInfo();
|
|
|
|
|
|
}
|
2025-03-18 12:33:54 +08:00
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 加载流程依赖
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="libraryfilePath"></param>
|
|
|
|
|
|
/// <exception cref="Exception"></exception>
|
|
|
|
|
|
public FlowLibraryInfo? LoadFlowLibrary(string libraryfilePath)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!CheckBaseLibrary(libraryfilePath, out var baseLibraryPath))
|
2025-03-18 12:33:54 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
throw new Exception($"从文件加载DLL失败,目标文件夹不存在{SereinBaseLibrary}文件");
|
2025-03-18 12:33:54 +08:00
|
|
|
|
}
|
2025-07-28 12:16:29 +08:00
|
|
|
|
FlowLibraryAssemblyContext flowAlc = new FlowLibraryAssemblyContext(baseLibraryPath, Path.GetFileName(libraryfilePath));
|
|
|
|
|
|
var flowAssembly = flowAlc.LoadFromAssemblyPath(libraryfilePath);
|
|
|
|
|
|
if(flowAssembly is null)
|
|
|
|
|
|
{
|
|
|
|
|
|
throw new Exception($"从文件加载DLL失败,FlowLibraryAssemblyContext 加载的程序集为 null \"{libraryfilePath}\"");
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var flowLibrary = new FlowLibraryCache(flowAssembly);
|
|
|
|
|
|
var isSuccess = flowLibrary.LoadFlowMethod();
|
|
|
|
|
|
if (!isSuccess)
|
2025-03-18 11:52:54 +08:00
|
|
|
|
{
|
|
|
|
|
|
flowAlc?.Unload(); // 卸载程序集
|
|
|
|
|
|
GC.Collect(); // 强制触发GC确保卸载成功
|
|
|
|
|
|
GC.WaitForPendingFinalizers();
|
2025-07-28 12:16:29 +08:00
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var assemblyName = flowAssembly.GetName().Name;
|
|
|
|
|
|
if (string.IsNullOrEmpty(assemblyName))
|
|
|
|
|
|
{
|
|
|
|
|
|
flowLibrary.Unload();
|
|
|
|
|
|
flowAlc?.Unload(); // 卸载程序集
|
|
|
|
|
|
GC.Collect(); // 强制触发GC确保卸载成功
|
|
|
|
|
|
GC.WaitForPendingFinalizers();
|
|
|
|
|
|
return null;
|
|
|
|
|
|
throw new Exception($"程序集\"{flowAssembly}\"返回 Name 为 null");
|
|
|
|
|
|
}
|
|
|
|
|
|
_flowLibraryCaches.TryAdd(assemblyName, flowLibrary);
|
|
|
|
|
|
return flowLibrary.ToInfo();
|
2025-03-18 11:52:54 +08:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-04 23:30:52 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 卸载类库
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="assemblyName"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public bool UnloadLibrary(string assemblyName)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
if (_flowLibraryCaches.Remove(assemblyName, out var flowLibrary))
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
try
|
|
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
flowLibrary.Unload(); // 尝试卸载
|
2024-11-04 23:30:52 +08:00
|
|
|
|
flowLibrary = null;
|
2024-11-03 21:17:45 +08:00
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
catch (Exception ex)
|
|
|
|
|
|
{
|
2025-07-17 22:46:40 +08:00
|
|
|
|
SereinEnv.WriteLine(InfoType.ERROR, $"尝试卸载程序集[{assemblyName}]发生错误:{ex.Message}");
|
2024-11-03 21:17:45 +08:00
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2024-11-03 23:51:18 +08:00
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
#region 获取流程依赖的相关方法
|
|
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取方法描述
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="assemblyName">程序集名称</param>
|
|
|
|
|
|
/// <param name="methodName">方法名称</param>
|
|
|
|
|
|
/// <param name="md">返回的方法描述</param>
|
|
|
|
|
|
/// <returns>是否获取成功</returns>
|
2025-07-07 20:40:24 +08:00
|
|
|
|
public bool TryGetMethodInfo(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodInfo methodInfo)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (string.IsNullOrEmpty(assemblyName) || string.IsNullOrEmpty(methodName))
|
|
|
|
|
|
{
|
|
|
|
|
|
methodInfo = null;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
2025-07-28 12:16:29 +08:00
|
|
|
|
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary)
|
2025-07-07 20:40:24 +08:00
|
|
|
|
&& flowLibrary.MethodInfos.TryGetValue(methodName, out methodInfo))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
methodInfo = null;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
/// <summary>
|
2025-07-07 20:40:24 +08:00
|
|
|
|
/// 获取方法描述
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="assemblyName">程序集名称</param>
|
|
|
|
|
|
/// <param name="methodName">方法名称</param>
|
|
|
|
|
|
/// <param name="md">返回的方法描述</param>
|
|
|
|
|
|
/// <returns>是否获取成功</returns>
|
2024-11-03 23:51:18 +08:00
|
|
|
|
public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary)
|
2024-11-03 23:51:18 +08:00
|
|
|
|
&& flowLibrary.MethodDetailss.TryGetValue(methodName, out md))
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
md = null;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
2024-11-03 23:51:18 +08:00
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取方法调用的委托
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="assemblyName">程序集名称</param>
|
|
|
|
|
|
/// <param name="methodName">方法名称</param>
|
|
|
|
|
|
/// <param name="dd">返回的委托调用封装类</param>
|
|
|
|
|
|
/// <returns>是否获取成功</returns>
|
2024-11-03 23:51:18 +08:00
|
|
|
|
public bool TryGetDelegateDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out DelegateDetails dd)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
&& flowLibrary.DelegateDetailss.TryGetValue(methodName, out dd))
|
|
|
|
|
|
{
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
dd = null;
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取(初始化/加载时/退出后)相应的节点方法
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <param name="nodeType"></param>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<MethodDetails> GetMdsOnFlowStart(NodeType nodeType)
|
|
|
|
|
|
{
|
|
|
|
|
|
List<MethodDetails> mds = [];
|
|
|
|
|
|
|
2025-07-28 12:16:29 +08:00
|
|
|
|
foreach (var library in _flowLibraryCaches.Values)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-03 23:51:18 +08:00
|
|
|
|
var t_mds = library.MethodDetailss.Values.Where(it => it.MethodDynamicType == nodeType).ToList();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
mds.AddRange(t_mds);
|
|
|
|
|
|
}
|
|
|
|
|
|
return mds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取流程启动时在不同时间点需要自动实例化的类型
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public Dictionary<RegisterSequence, List<Type>> GetaAutoRegisterType()
|
|
|
|
|
|
{
|
|
|
|
|
|
Dictionary<RegisterSequence, List<Type>> rsTypes = new Dictionary<RegisterSequence, List<Type>>();
|
2025-07-28 12:16:29 +08:00
|
|
|
|
foreach (var library in _flowLibraryCaches.Values)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2024-11-03 23:51:18 +08:00
|
|
|
|
foreach (var kv in library.RegisterTypes)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
var @class = kv.Key;
|
|
|
|
|
|
var type = kv.Value;
|
2024-11-03 23:51:18 +08:00
|
|
|
|
if (!rsTypes.TryGetValue(@class, out var tmpTypes))
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
|
|
|
|
|
tmpTypes = new List<Type>();
|
|
|
|
|
|
rsTypes.Add(@class, tmpTypes);
|
|
|
|
|
|
}
|
|
|
|
|
|
tmpTypes.AddRange(type);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return rsTypes;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-11-04 23:30:52 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 获取某个程序集下的所有方法信息,用于保存项目时调用
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
|
|
|
|
|
public List<MethodDetails> GetLibraryMdsOfAssmbly(string assemblyName)
|
|
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
if (_flowLibraryCaches.TryGetValue(assemblyName, out var flowLibrary))
|
2024-11-04 23:30:52 +08:00
|
|
|
|
{
|
|
|
|
|
|
return flowLibrary.MethodDetailss.Values.ToList();
|
|
|
|
|
|
}
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
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>
|
|
|
|
|
|
/// <returns></returns>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public List<FlowLibraryInfo> GetAllLibraryMds()
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
List<FlowLibraryInfo> mds = new List<FlowLibraryInfo>();
|
|
|
|
|
|
foreach (FlowLibraryCache library in _flowLibraryCaches.Values)
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
var tmp = new FlowLibraryInfo
|
2024-11-03 23:51:18 +08:00
|
|
|
|
{
|
|
|
|
|
|
AssemblyName = library.FullName,
|
2025-07-28 12:16:29 +08:00
|
|
|
|
MethodInfos = library.MethodDetailss.Values.Select(md => md.ToInfo()).ToList()
|
2024-11-03 21:17:45 +08:00
|
|
|
|
};
|
|
|
|
|
|
mds.Add(tmp);
|
|
|
|
|
|
}
|
|
|
|
|
|
return mds;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 序列化当前项目的依赖信息、节点信息,用于远程登录的场景,需要将依赖信息从本地(受控端)发送到远程(主控端)
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns></returns>
|
2025-07-28 12:16:29 +08:00
|
|
|
|
public List<FlowLibraryInfo> GetAllLibraryInfo()
|
2024-11-03 21:17:45 +08:00
|
|
|
|
{
|
2025-07-28 12:16:29 +08:00
|
|
|
|
return _flowLibraryCaches.Values.Select(library => library.ToInfo()).ToList();
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|
2025-07-28 12:16:29 +08:00
|
|
|
|
#endregion
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
|
|
|
|
|
#region 功能性方法
|
|
|
|
|
|
|
2024-12-26 00:26:50 +08:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// 基础依赖
|
|
|
|
|
|
/// </summary>
|
2025-07-07 20:40:24 +08:00
|
|
|
|
public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Library)}.dll";
|
2024-11-04 23:30:52 +08:00
|
|
|
|
|
2024-11-03 21:17:45 +08:00
|
|
|
|
|
2024-11-03 23:51:18 +08:00
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
2024-11-03 21:17:45 +08:00
|
|
|
|
}
|