mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
完善节点图的代码生成
This commit is contained in:
@@ -14,10 +14,18 @@ using static System.Runtime.InteropServices.JavaScript.JSType;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
public class LibraryMdDd (MethodDetails methodDetails,DelegateDetails delegateDetails)
|
||||
public class LibraryMdDd
|
||||
{
|
||||
public MethodDetails MethodDetails { get; } = methodDetails;
|
||||
public DelegateDetails DelegateDetails { get; } = delegateDetails;
|
||||
public MethodDetails MethodDetails { get; }
|
||||
public MethodInfo MethodInfo { get; }
|
||||
public DelegateDetails DelegateDetails { get; }
|
||||
|
||||
public LibraryMdDd(MethodInfo methodInfo, MethodDetails methodDetails, DelegateDetails delegateDetails)
|
||||
{
|
||||
MethodDetails = methodDetails;
|
||||
MethodInfo = methodInfo;
|
||||
DelegateDetails = delegateDetails;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,8 +36,6 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
public Assembly Assembly { get; private set; }
|
||||
|
||||
|
||||
|
||||
//private readonly Action actionOfUnloadAssmbly;
|
||||
/*, Action actionOfUnloadAssmbly*/
|
||||
//this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
|
||||
@@ -60,6 +66,7 @@ namespace Serein.NodeFlow
|
||||
/// Value :方法详情
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = new ConcurrentDictionary<string, MethodDetails>();
|
||||
public ConcurrentDictionary<string, MethodInfo> MethodInfos { get; } = new ConcurrentDictionary<string, MethodInfo>();
|
||||
|
||||
/// <summary>
|
||||
/// 管理通过Emit动态构建的委托
|
||||
@@ -112,6 +119,7 @@ namespace Serein.NodeFlow
|
||||
public bool LoadAssembly()
|
||||
{
|
||||
Assembly assembly = this.Assembly;
|
||||
|
||||
#region 检查入参
|
||||
|
||||
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
||||
@@ -142,7 +150,7 @@ namespace Serein.NodeFlow
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
#region 获取 DynamicFlow 特性的流程控制器,如果没有退出
|
||||
// Type : 具有 DynamicFlowAttribute 标记的类型
|
||||
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性 (用于生成方法描述时,添加在方法别名中提高可读性)
|
||||
@@ -183,13 +191,13 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
// 尝试创建
|
||||
if (!NodeMethodDetailsHelper.TryCreateDetails(type, methodInfo, assemblyName,
|
||||
out var md, out var dd)) // 返回的描述
|
||||
out var mi, out var md, out var dd)) // 返回的描述
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"无法加载方法信息:{assemblyName}-{type}-{methodInfo}");
|
||||
continue;
|
||||
}
|
||||
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
|
||||
detailss.Add(new LibraryMdDd(md, dd));
|
||||
detailss.Add(new LibraryMdDd(mi, md, dd));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -219,6 +227,7 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
var key = item.MethodDetails.MethodName;
|
||||
MethodDetailss.TryAdd(key, item.MethodDetails);
|
||||
MethodInfos.TryAdd(key, item.MethodInfo);
|
||||
DelegateDetailss.TryAdd(key, item.DelegateDetails);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,371 +0,0 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.FlowNode;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Xml.Linq;
|
||||
|
||||
namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理加载在运行环境中的外部程序集
|
||||
/// </summary>
|
||||
internal class FlowLibraryService
|
||||
{
|
||||
public FlowLibraryService(IFlowEnvironment flowEnvironment)
|
||||
{
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
}
|
||||
|
||||
private readonly IFlowEnvironment flowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存所有加载了的程序集
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, FlowLibrary> _myFlowLibrarys = new ConcurrentDictionary<string, FlowLibrary>();
|
||||
|
||||
/// <summary>
|
||||
/// 加载类库
|
||||
/// </summary>
|
||||
/// <param name="libraryfilePath"></param>
|
||||
/// <returns></returns>
|
||||
public (NodeLibraryInfo, List<MethodDetailsInfo>) LoadLibraryOfPath(string libraryfilePath)
|
||||
{
|
||||
|
||||
var dir = Path.GetDirectoryName(libraryfilePath); // 获取目录路径
|
||||
var sereinFlowBaseLibraryPath = Path.Combine(dir, SereinBaseLibrary);// 每个类库下面至少需要有“Serein.Library.dll”类库依赖
|
||||
if (!Path.Exists(sereinFlowBaseLibraryPath))
|
||||
{
|
||||
throw new Exception($"从文件加载DLL失败,目标文件夹不存在{SereinBaseLibrary}文件" );
|
||||
}
|
||||
|
||||
var flowAlc = new FlowLibraryAssemblyContext(sereinFlowBaseLibraryPath, Path.GetFileName(libraryfilePath));
|
||||
var assembly = flowAlc.LoadFromAssemblyPath(libraryfilePath); // 加载指定路径的程序集
|
||||
|
||||
var flowLibrary = new FlowLibrary(assembly);
|
||||
try
|
||||
{
|
||||
var reulst = LoadFlowLibrary(flowLibrary);
|
||||
return reulst;
|
||||
}
|
||||
catch (Exception)
|
||||
{
|
||||
flowAlc?.Unload(); // 卸载程序集
|
||||
flowAlc = null;
|
||||
GC.Collect(); // 强制触发GC确保卸载成功
|
||||
GC.WaitForPendingFinalizers();
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 加载类库
|
||||
/// </summary>
|
||||
/// <param name="flowLibrary"></param>
|
||||
/// <returns></returns>
|
||||
public (NodeLibraryInfo, List<MethodDetailsInfo>) LoadLibraryOfPath(FlowLibrary flowLibrary)
|
||||
{
|
||||
return LoadFlowLibrary(flowLibrary);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 卸载类库
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"></param>
|
||||
/// <returns></returns>
|
||||
public bool UnloadLibrary(string assemblyName)
|
||||
{
|
||||
if (_myFlowLibrarys.Remove(assemblyName, out var flowLibrary))
|
||||
{
|
||||
try
|
||||
{
|
||||
flowLibrary.Upload(); // 尝试卸载
|
||||
flowLibrary = null;
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"尝试卸载程序集[{assemblyName}]发生错误:{ex}");
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取方法描述
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集名称</param>
|
||||
/// <param name="methodName">方法名称</param>
|
||||
/// <param name="md">返回的方法描述</param>
|
||||
/// <returns>是否获取成功</returns>
|
||||
public bool TryGetMethodDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out MethodDetails md)
|
||||
{
|
||||
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
|
||||
&& flowLibrary.MethodDetailss.TryGetValue(methodName, out md))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
md = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取方法调用的委托
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集名称</param>
|
||||
/// <param name="methodName">方法名称</param>
|
||||
/// <param name="dd">返回的委托调用封装类</param>
|
||||
/// <returns>是否获取成功</returns>
|
||||
public bool TryGetDelegateDetails(string assemblyName, string methodName, [MaybeNullWhen(false)] out DelegateDetails dd)
|
||||
{
|
||||
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary)
|
||||
&& 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 = [];
|
||||
|
||||
foreach (var library in _myFlowLibrarys.Values)
|
||||
{
|
||||
var t_mds = library.MethodDetailss.Values.Where(it => it.MethodDynamicType == nodeType).ToList();
|
||||
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>>();
|
||||
foreach (var library in _myFlowLibrarys.Values)
|
||||
{
|
||||
foreach (var kv in library.RegisterTypes)
|
||||
{
|
||||
var @class = kv.Key;
|
||||
var type = kv.Value;
|
||||
if (!rsTypes.TryGetValue(@class, out var tmpTypes))
|
||||
{
|
||||
tmpTypes = new List<Type>();
|
||||
rsTypes.Add(@class, tmpTypes);
|
||||
}
|
||||
tmpTypes.AddRange(type);
|
||||
}
|
||||
}
|
||||
return rsTypes;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取某个程序集下的所有方法信息,用于保存项目时调用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<MethodDetails> GetLibraryMdsOfAssmbly(string assemblyName)
|
||||
{
|
||||
if (_myFlowLibrarys.TryGetValue(assemblyName, out var flowLibrary))
|
||||
{
|
||||
return flowLibrary.MethodDetailss.Values.ToList();
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 获取所有方法信息,用于保存项目时调用
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<LibraryMds> GetAllLibraryMds()
|
||||
{
|
||||
List<LibraryMds> mds = new List<LibraryMds>();
|
||||
foreach (FlowLibrary library in _myFlowLibrarys.Values)
|
||||
{
|
||||
var tmp = new LibraryMds
|
||||
{
|
||||
AssemblyName = library.FullName,
|
||||
Mds = library.MethodDetailss.Values.Select(md => md.ToInfo()).ToArray()
|
||||
};
|
||||
mds.Add(tmp);
|
||||
}
|
||||
return mds;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 序列化当前项目的依赖信息、节点信息,用于远程登录的场景,需要将依赖信息从本地(受控端)发送到远程(主控端)
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public List<NodeLibraryInfo> GetAllLibraryInfo()
|
||||
{
|
||||
return _myFlowLibrarys.Values.Select(library => library.ToInfo()).ToList();
|
||||
}
|
||||
|
||||
|
||||
#region 功能性方法
|
||||
|
||||
/// <summary>
|
||||
/// 基础依赖
|
||||
/// </summary>
|
||||
public readonly static string SereinBaseLibrary = $"{nameof(Serein)}.{nameof(Serein.Library)}.dll";
|
||||
|
||||
//private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadDllNodeInfo(Assembly assembly)
|
||||
//{
|
||||
|
||||
// if (assembly.FullName?.ToString().Equals(typeof(IFlowEnvironment).Assembly.FullName?.ToString()) == true)
|
||||
// {
|
||||
|
||||
// // 加载基础依赖
|
||||
// return LoadAssembly(typeof(IFlowEnvironment).Assembly);
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// try
|
||||
// {
|
||||
// var assembly_result = LoadAssembly(assembly);
|
||||
// return assembly_result;
|
||||
// }
|
||||
// catch (Exception)
|
||||
// {
|
||||
// return (null,[]);
|
||||
// }
|
||||
|
||||
// }
|
||||
|
||||
//}
|
||||
|
||||
|
||||
|
||||
private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadFlowLibrary(FlowLibrary flowLibrary)
|
||||
{
|
||||
var assembly = flowLibrary.Assembly;
|
||||
if (assembly.FullName?.ToString().Equals(typeof(IFlowEnvironment).Assembly.FullName?.ToString()) == true)
|
||||
{
|
||||
// 加载基础依赖
|
||||
flowLibrary = new FlowLibrary(typeof(IFlowEnvironment).Assembly);
|
||||
}
|
||||
|
||||
var assmblyName = assembly.GetName().Name;
|
||||
if (!string.IsNullOrEmpty(assmblyName) && _myFlowLibrarys.ContainsKey(assmblyName))
|
||||
{
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]已经加载过!");
|
||||
}
|
||||
|
||||
var loadResult = flowLibrary.LoadAssembly(); // 加载程序集
|
||||
if (loadResult)
|
||||
{
|
||||
var assemblyName = assembly.GetName().Name;
|
||||
if (string.IsNullOrEmpty(assemblyName))
|
||||
{
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败,没有程序集名称");
|
||||
}
|
||||
_myFlowLibrarys.TryAdd(assemblyName, flowLibrary);
|
||||
|
||||
List<MethodDetailsInfo> mdInfos = flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList();
|
||||
mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase));
|
||||
|
||||
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(), mdInfos);
|
||||
return result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 流程依赖加载
|
||||
/// </summary>
|
||||
public class FlowLibraryAssemblyContext : AssemblyLoadContext
|
||||
{
|
||||
private readonly AssemblyDependencyResolver _resolver;
|
||||
|
||||
/// <summary>
|
||||
/// 创建新的加载上下文
|
||||
/// </summary>
|
||||
/// <param name="sereinFlowLibraryPath">类库路径</param>
|
||||
/// <param name="name"></param>
|
||||
public FlowLibraryAssemblyContext(string sereinFlowLibraryPath, string name) : base(name, isCollectible: true)
|
||||
{
|
||||
_resolver = new AssemblyDependencyResolver(sereinFlowLibraryPath);
|
||||
}
|
||||
|
||||
protected override Assembly? Load(AssemblyName assemblyName)
|
||||
{
|
||||
string? assemblyPath = _resolver.ResolveAssemblyToPath(assemblyName);
|
||||
if (!string.IsNullOrEmpty(assemblyPath))
|
||||
{
|
||||
var assembly = AssemblyLoadContext.Default.LoadFromAssemblyPath(assemblyPath);
|
||||
//var assembly = LoadFromAssemblyPath(assemblyPath);
|
||||
return assembly;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Default.Assemblies.FirstOrDefault(x => x.FullName == assemblyName.FullName);
|
||||
}
|
||||
|
||||
// return null;
|
||||
|
||||
// 构建依赖项的路径
|
||||
//string assemblyPath = Path.Combine(AppContext.BaseDirectory, assemblyName.Name + ".dll");
|
||||
//if (File.Exists(assemblyPath))
|
||||
//{
|
||||
// return LoadFromAssemblyPath(assemblyPath);
|
||||
//}
|
||||
//assemblyPath = Path.Combine(filePath, assemblyName.Name + ".dll");
|
||||
//if (File.Exists(assemblyPath))
|
||||
//{
|
||||
// return LoadFromAssemblyPath(assemblyPath);
|
||||
//}
|
||||
|
||||
//return null; // 如果没有找到,返回 null
|
||||
}
|
||||
}
|
||||
//public static class PluginAssemblyContextExtensions
|
||||
//{
|
||||
|
||||
// public static Assembly FromAssemblyPath(this AssemblyLoadContext context, string path)
|
||||
// {
|
||||
|
||||
// return context.LoadFromAssemblyPath(path);
|
||||
|
||||
// }
|
||||
|
||||
//}
|
||||
}
|
||||
@@ -35,6 +35,7 @@ public static class NodeMethodDetailsHelper
|
||||
public static bool TryCreateDetails(Type type,
|
||||
MethodInfo methodInfo,
|
||||
string assemblyName,
|
||||
[MaybeNullWhen(false)] out MethodInfo outMethodInfo,
|
||||
[MaybeNullWhen(false)] out MethodDetails methodDetails,
|
||||
[MaybeNullWhen(false)] out DelegateDetails delegateDetails)
|
||||
{
|
||||
@@ -43,6 +44,7 @@ public static class NodeMethodDetailsHelper
|
||||
var attribute = methodInfo.GetCustomAttribute<NodeActionAttribute>();
|
||||
if(attribute is null || attribute.Scan == false)
|
||||
{
|
||||
outMethodInfo = null;
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
@@ -62,17 +64,18 @@ public static class NodeMethodDetailsHelper
|
||||
|
||||
|
||||
Type? returnType;
|
||||
bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult);
|
||||
bool isAsync = IsGenericTask(methodInfo.ReturnType, out var taskResult);
|
||||
|
||||
|
||||
if (attribute.MethodDynamicType == Library.NodeType.UI)
|
||||
{
|
||||
if (isTask)
|
||||
if (isAsync)
|
||||
{
|
||||
var innerType = methodInfo.ReturnType.GetGenericArguments()[0];
|
||||
if (innerType.IsGenericType && innerType != typeof(IEmbeddedContent))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为UI方法的返回值并非IEmbeddedContent,流程工作台将无法正确显示自定义控件界面以及传递数据。");
|
||||
outMethodInfo = null;
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
@@ -83,6 +86,7 @@ public static class NodeMethodDetailsHelper
|
||||
if (methodInfo.ReturnType != typeof(IEmbeddedContent))
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为UI方法的返回值并非IEmbeddedContent,流程工作台将无法正确显示自定义控件界面以及传递数据。");
|
||||
outMethodInfo = null;
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
@@ -106,6 +110,7 @@ public static class NodeMethodDetailsHelper
|
||||
else
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,返回类型非预期的Task<IFlipflopContext<TResult>>。");
|
||||
outMethodInfo = null;
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
@@ -114,13 +119,14 @@ public static class NodeMethodDetailsHelper
|
||||
else
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建,因为触发器方法的返回值并非Task<>,将无法等待。");
|
||||
outMethodInfo = null;
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
else if(isTask)
|
||||
else if(isAsync)
|
||||
{
|
||||
returnType = taskResult is null ? typeof(Task) : taskResult;
|
||||
}
|
||||
@@ -134,7 +140,7 @@ public static class NodeMethodDetailsHelper
|
||||
}
|
||||
|
||||
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
|
||||
var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
|
||||
var methodMethodAnotherName = isAsync ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
|
||||
|
||||
bool hasParamsArg = false;
|
||||
if (explicitDataOfParameters.Length > 0)
|
||||
@@ -155,13 +161,14 @@ public static class NodeMethodDetailsHelper
|
||||
ReturnType = returnType,
|
||||
// 如果存在可变参数,取最后一个元素的下标,否则为-1;
|
||||
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1,
|
||||
IsAsync = isAsync,
|
||||
};
|
||||
|
||||
//var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
|
||||
|
||||
|
||||
var dd = new DelegateDetails(methodInfo) ; // 构造委托
|
||||
|
||||
outMethodInfo = methodInfo;
|
||||
methodDetails = md;
|
||||
delegateDetails = dd;
|
||||
return true;
|
||||
|
||||
Reference in New Issue
Block a user