mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
改写了流程依赖管理,封装为一个工具类,将来计划实现动态增加卸载/更新类库的功能
This commit is contained in:
@@ -676,8 +676,8 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 移除DLL
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集的名称</param>
|
||||
bool RemoteDll(string assemblyName);
|
||||
/// <param name="assemblyFullName">程序集的名称</param>
|
||||
bool RemoteDll(string assemblyFullName);
|
||||
|
||||
/// <summary>
|
||||
/// 清理加载的DLL(待更改)
|
||||
@@ -860,7 +860,7 @@ namespace Serein.Library.Api
|
||||
/// <param name="methodName">方法描述</param>
|
||||
/// <param name="mdInfo">方法信息</param>
|
||||
/// <returns></returns>
|
||||
bool TryGetMethodDetailsInfo(string methodName, out MethodDetailsInfo mdInfo);
|
||||
bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定方法的Emit委托
|
||||
@@ -868,7 +868,7 @@ namespace Serein.Library.Api
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="del"></param>
|
||||
/// <returns></returns>
|
||||
bool TryGetDelegateDetails(string methodName, out DelegateDetails del);
|
||||
bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del);
|
||||
|
||||
|
||||
#region 远程相关
|
||||
|
||||
@@ -1,108 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 节点DLL依赖类,如果一个项目中引入了多个DLL,需要放置在同一个文件夹中
|
||||
/// </summary>
|
||||
public class NodeLibraryInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 路径
|
||||
/// </summary>
|
||||
public string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 所属的程序集名称
|
||||
/// </summary>
|
||||
public string AssemblyName{ get; set; }
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FlowLibrary
|
||||
{
|
||||
public FlowLibrary(string assemblyName,
|
||||
Action actionOfUnloadAssmbly)
|
||||
{
|
||||
this.AssemblyName = assemblyName;
|
||||
this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
|
||||
}
|
||||
|
||||
public string AssemblyName { get; }
|
||||
|
||||
//public string AssemblyVersion { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 加载程序集时创建的方法描述
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = new ConcurrentDictionary<string, MethodDetails>();
|
||||
|
||||
/// <summary>
|
||||
/// 管理通过Emit动态构建的委托
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, DelegateDetails> DelegateDetailss { get; } = new ConcurrentDictionary<string, DelegateDetails>();
|
||||
|
||||
/// <summary>
|
||||
/// 记录不同的注册时机需要自动创建全局唯一实例的类型信息
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<RegisterSequence, Type[]> RegisterTypes { get; } = new ConcurrentDictionary<RegisterSequence, Type[]>();
|
||||
|
||||
|
||||
private readonly Action actionOfUnloadAssmbly;
|
||||
|
||||
/// <summary>
|
||||
/// 卸载当前程序集以及附带的所有信息
|
||||
/// </summary>
|
||||
public void Upload()
|
||||
{
|
||||
actionOfUnloadAssmbly?.Invoke();
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 通过方法名称获取对应的Emit委托(元数据),用于动态调用节点对应的方法
|
||||
/// </summary>
|
||||
/// <param name="methodName">方法名称</param>
|
||||
/// <param name="dd">Emit委托</param>
|
||||
/// <returns></returns>
|
||||
public bool GetDelegateDetails(string methodName, out DelegateDetails dd)
|
||||
{
|
||||
return DelegateDetailss.TryGetValue(methodName, out dd);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过方法名称获取对应的方法描述(元数据),用于创建节点时,节点实例需要的方法描述
|
||||
/// </summary>
|
||||
/// <param name="methodName">方法名称</param>
|
||||
/// <param name="md">方法描述</param>
|
||||
/// <returns></returns>
|
||||
public bool GetMethodDetails(string methodName, out MethodDetails md)
|
||||
{
|
||||
return MethodDetailss.TryGetValue(methodName, out md);
|
||||
}
|
||||
|
||||
public NodeLibraryInfo ToInfo()
|
||||
{
|
||||
return new NodeLibraryInfo
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -2,6 +2,7 @@
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.Library
|
||||
@@ -21,6 +22,13 @@ namespace Serein.Library
|
||||
[PropertyInfo(IsProtection = true)]
|
||||
private NodeModelBase _nodeModel;
|
||||
|
||||
/// <summary>
|
||||
/// 对应的程序集
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private string _assemblyName;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 是否保护参数(目前仅视觉效果参数,不影响运行实现,后续将设置作用在运行逻辑中)
|
||||
/// </summary>
|
||||
@@ -188,6 +196,7 @@ namespace Serein.Library
|
||||
{
|
||||
throw new ArgumentException("无效的节点类型");
|
||||
}
|
||||
AssemblyName = Info.AssemblyName;
|
||||
MethodName = Info.MethodName;
|
||||
MethodAnotherName = Info.MethodAnotherName;
|
||||
MethodDynamicType = nodeType;
|
||||
@@ -204,6 +213,7 @@ namespace Serein.Library
|
||||
{
|
||||
return new MethodDetailsInfo
|
||||
{
|
||||
AssemblyName = this.AssemblyName,
|
||||
MethodName = this.MethodName,
|
||||
MethodAnotherName = this.MethodAnotherName,
|
||||
NodeType = this.MethodDynamicType.ToString(),
|
||||
@@ -222,6 +232,7 @@ namespace Serein.Library
|
||||
// this => 是元数据
|
||||
var md = new MethodDetails( nodeModel) // 创建新节点时拷贝实例
|
||||
{
|
||||
AssemblyName = this.AssemblyName,
|
||||
ActingInstance = this.ActingInstance,
|
||||
ActingInstanceType = this.ActingInstanceType,
|
||||
MethodDynamicType = this.MethodDynamicType,
|
||||
@@ -231,8 +242,9 @@ namespace Serein.Library
|
||||
MethodLockName = this.MethodLockName,
|
||||
IsProtectionParameter = this.IsProtectionParameter,
|
||||
ParamsArgIndex = this.ParamsArgIndex,
|
||||
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述
|
||||
};
|
||||
md.ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(); // 拷贝属于节点方法的新入参描述
|
||||
|
||||
return md;
|
||||
}
|
||||
|
||||
|
||||
@@ -52,6 +52,7 @@ namespace Serein.Library
|
||||
return new NodeInfo
|
||||
{
|
||||
Guid = Guid,
|
||||
AssemblyName = MethodDetails.AssemblyName,
|
||||
MethodName = MethodDetails?.MethodName,
|
||||
Label = MethodDetails?.MethodAnotherName,
|
||||
Type = this.GetType().ToString(),
|
||||
@@ -316,7 +317,7 @@ namespace Serein.Library
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
}
|
||||
if (!context.Env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||
}
|
||||
|
||||
@@ -61,7 +61,7 @@ namespace Serein.Library
|
||||
/// 依赖的DLL
|
||||
/// </summary>
|
||||
|
||||
public Library[] Librarys { get; set; }
|
||||
public NodeLibraryInfo[] Librarys { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 起始节点GUID
|
||||
@@ -132,38 +132,63 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 项目依赖的程序集,项目文件相关
|
||||
/// </summary>
|
||||
public class Library
|
||||
/// <summary>
|
||||
public class NodeLibraryInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名称
|
||||
/// 文件名
|
||||
/// </summary>
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
/// 路径
|
||||
/// </summary>
|
||||
public string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 程序集名称
|
||||
/// 所属的程序集名称
|
||||
/// </summary>
|
||||
public string AssemblyName { get; set; }
|
||||
}
|
||||
|
||||
#region 暂时注释
|
||||
/*public class LibraryInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// 文件名称
|
||||
/// </summary>
|
||||
|
||||
public string FileName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 文件路径
|
||||
/// </summary>
|
||||
public string FilePath { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 程序集名称
|
||||
/// </summary>
|
||||
public string AssemblyName { get; set; }
|
||||
}*/
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 节点信息,项目文件相关
|
||||
/// </summary>
|
||||
public class NodeInfo
|
||||
{
|
||||
/// <summary>
|
||||
/// GUID
|
||||
/// 节点的GUID
|
||||
/// </summary>
|
||||
|
||||
public string Guid { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 名称
|
||||
/// 节点方法所属的程序集名称
|
||||
/// </summary>
|
||||
public string AssemblyName { get;set; }
|
||||
|
||||
/// <summary>
|
||||
/// 节点对应的名称
|
||||
/// </summary>
|
||||
|
||||
public string MethodName { get; set; }
|
||||
|
||||
@@ -9,6 +9,7 @@ using Serein.Library.Utils.SereinExpression;
|
||||
using Serein.NodeFlow.Model;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Diagnostics;
|
||||
using System.Numerics;
|
||||
using System.Reflection;
|
||||
using System.Reflection.Metadata.Ecma335;
|
||||
@@ -52,7 +53,8 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
|
||||
};
|
||||
this.UIContextOperation = uiContextOperation; // 本地环境需要存放视图管理
|
||||
this.UIContextOperation = uiContextOperation; // 为加载的类库提供在UI线程上执行某些操作的封装工具类
|
||||
this.FlowLibraryManagement = new FlowLibraryManagement(this); // 实例化类库管理
|
||||
}
|
||||
|
||||
#region 远程管理
|
||||
@@ -227,14 +229,16 @@ namespace Serein.NodeFlow.Env
|
||||
/// <summary>
|
||||
/// 通过程序集名称管理动态加载的程序集,用于节点创建提供方法描述,流程运行时提供Emit委托
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, FlowLibrary> FlowLibrarys { get; } = [];
|
||||
private readonly FlowLibraryManagement FlowLibraryManagement;
|
||||
|
||||
#if false
|
||||
|
||||
/// <summary>
|
||||
/// Library 与 MethodDetailss的依赖关系
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<NodeLibraryInfo, List<MethodDetails>> MethodDetailsOfLibraryInfos { get; } = [];
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// <para>存储已加载的程序集</para>
|
||||
/// <para>Key:程序集的FullName </para>
|
||||
@@ -258,7 +262,8 @@ namespace Serein.NodeFlow.Env
|
||||
/// 存放所有通过Emit加载的委托
|
||||
/// md.Methodname - delegate
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<string, DelegateDetails> MethodDelegates { get; } = [];
|
||||
private ConcurrentDictionary<string, DelegateDetails> MethodDelegates { get; } = [];
|
||||
#endif
|
||||
|
||||
/// <summary>
|
||||
/// IOC对象容器管理
|
||||
@@ -351,17 +356,13 @@ namespace Serein.NodeFlow.Env
|
||||
flowStarter = new FlowStarter();
|
||||
var nodes = NodeModels.Values.ToList();
|
||||
|
||||
List<MethodDetails> initMethods = [];
|
||||
List<MethodDetails> loadMethods = [];
|
||||
List<MethodDetails> exitMethods = [];
|
||||
|
||||
|
||||
var initMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Init);
|
||||
var loadMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Loading);
|
||||
var exitMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Exit);
|
||||
List<MethodDetails> initMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Init);
|
||||
List<MethodDetails> loadMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Loading);
|
||||
List<MethodDetails> exitMethods = this.FlowLibraryManagement.GetMdsOnFlowStart(NodeType.Exit);
|
||||
Dictionary<RegisterSequence, List<Type>> autoRegisterTypes = this.FlowLibraryManagement.GetaAutoRegisterType();
|
||||
|
||||
initMethods.AddRange(initMds);
|
||||
loadMethods.AddRange(loadMds);
|
||||
exitMethods.AddRange(exitMds);
|
||||
|
||||
IOC.Reset(); // 开始运行时清空ioc中注册的实例
|
||||
|
||||
@@ -369,7 +370,7 @@ namespace Serein.NodeFlow.Env
|
||||
if (this.UIContextOperation is not null)
|
||||
IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false);
|
||||
|
||||
await flowStarter.RunAsync(this, nodes, AutoRegisterTypes, initMethods, loadMethods, exitMethods);
|
||||
await flowStarter.RunAsync(this, nodes, autoRegisterTypes, initMethods, loadMethods, exitMethods);
|
||||
|
||||
if (FlipFlopState == RunState.Completion)
|
||||
{
|
||||
@@ -480,30 +481,9 @@ namespace Serein.NodeFlow.Env
|
||||
/// <returns></returns>
|
||||
public async Task<FlowEnvInfo> GetEnvInfoAsync()
|
||||
{
|
||||
Dictionary<NodeLibraryInfo, List<MethodDetailsInfo>> MdsOfLibraryInfos = [];
|
||||
|
||||
foreach (var mdskv in MethodDetailsOfLibraryInfos)
|
||||
{
|
||||
var library = mdskv.Key;
|
||||
var mds = mdskv.Value;
|
||||
foreach (var md in mds)
|
||||
{
|
||||
if (!MdsOfLibraryInfos.TryGetValue(library, out var t_mds))
|
||||
{
|
||||
t_mds = new List<MethodDetailsInfo>();
|
||||
MdsOfLibraryInfos[library] = t_mds;
|
||||
}
|
||||
var mdInfo = md.ToInfo();
|
||||
mdInfo.AssemblyName = library.AssemblyName;
|
||||
t_mds.Add(mdInfo);
|
||||
}
|
||||
}
|
||||
|
||||
LibraryMds[] libraryMdss = MdsOfLibraryInfos.Select(kv => new LibraryMds
|
||||
{
|
||||
AssemblyName = kv.Key.AssemblyName,
|
||||
Mds = kv.Value.ToArray()
|
||||
}).ToArray();
|
||||
// 获取所有的程序集对应的方法信息(程序集相关的数据)
|
||||
var libraryMdss = this.FlowLibraryManagement.GetAllLibraryMds().ToArray();
|
||||
// 获取当前项目的信息(节点相关的数据)
|
||||
var project = await GetProjectInfoAsync();
|
||||
Console.WriteLine("已将当前环境信息发送到远程客户端");
|
||||
return new FlowEnvInfo
|
||||
@@ -540,13 +520,13 @@ namespace Serein.NodeFlow.Env
|
||||
foreach (var dllPath in dllPaths)
|
||||
{
|
||||
var dllFilePath = Path.GetFullPath(Path.Combine(filePath, dllPath));
|
||||
LoadDllNodeInfo(dllFilePath);
|
||||
LoadDll(dllFilePath); // 加载项目文件时加载对应的程序集
|
||||
}
|
||||
|
||||
List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>();
|
||||
List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>();
|
||||
// 加载节点
|
||||
foreach (var nodeInfo in projectData.Nodes)
|
||||
foreach (NodeInfo? nodeInfo in projectData.Nodes)
|
||||
{
|
||||
var controlType = FlowFunc.GetNodeControlType(nodeInfo);
|
||||
if (controlType == NodeControlType.None)
|
||||
@@ -555,11 +535,14 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
else
|
||||
{
|
||||
MethodDetails? methodDetails = null;
|
||||
if (!string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||
if (string.IsNullOrEmpty(nodeInfo.AssemblyName) && string.IsNullOrEmpty(nodeInfo.MethodName))
|
||||
{
|
||||
MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载项目时尝试获取方法信息
|
||||
continue;
|
||||
}
|
||||
MethodDetails? methodDetails = null;
|
||||
FlowLibraryManagement.TryGetMethodDetails(nodeInfo.AssemblyName,
|
||||
nodeInfo.MethodName,
|
||||
out methodDetails); // 加载项目时尝试获取方法信息
|
||||
var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点
|
||||
nodeModel.LoadInfo(nodeInfo); // 创建节点model
|
||||
if (nodeModel is null)
|
||||
@@ -726,7 +709,7 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
var projectData = new SereinProjectData()
|
||||
{
|
||||
Librarys = LibraryInfos.Values.Select(lib => lib.ToLibrary()).ToArray(),
|
||||
Librarys = this.FlowLibraryManagement.GetAllLibraryInfo().ToArray(),
|
||||
Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(),
|
||||
StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid,
|
||||
};
|
||||
@@ -739,10 +722,11 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <param name="dllPath"></param>
|
||||
/// <returns></returns>
|
||||
// [AutoSocketHandle]
|
||||
public void LoadDll(string dllPath)
|
||||
{
|
||||
LoadDllNodeInfo(dllPath);
|
||||
(var libraryInfo, var mdInfos) = FlowLibraryManagement.LoadLibrary(dllPath);
|
||||
UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(libraryInfo, mdInfos))); // 通知UI创建dll面板显示
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -752,49 +736,51 @@ namespace Serein.NodeFlow.Env
|
||||
/// <returns></returns>
|
||||
public bool RemoteDll(string assemblyName)
|
||||
{
|
||||
var library = LibraryInfos.Values.FirstOrDefault(nl => assemblyName.Equals(nl.AssemblyName));
|
||||
if (library is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
var groupedNodes = NodeModels.Values
|
||||
.Where(node => node.MethodDetails is not null)
|
||||
.ToArray()
|
||||
.GroupBy(node => node.MethodDetails?.MethodName)
|
||||
.ToDictionary(
|
||||
key => key.Key,
|
||||
group => group.Count());
|
||||
return FlowLibraryManagement.UnloadLibrary(assemblyName);
|
||||
|
||||
//var library = LibraryInfos.Values.FirstOrDefault(nl => assemblyName.Equals(nl.AssemblyName));
|
||||
//if (library is null)
|
||||
//{
|
||||
// return false;
|
||||
//}
|
||||
//var groupedNodes = NodeModels.Values
|
||||
// .Where(node => node.MethodDetails is not null)
|
||||
// .ToArray()
|
||||
// .GroupBy(node => node.MethodDetails?.MethodName)
|
||||
// .ToDictionary(
|
||||
// key => key.Key,
|
||||
// group => group.Count());
|
||||
|
||||
|
||||
if (NodeModels.Count == 0)
|
||||
{
|
||||
return true; // 当前无节点,可以直接删除
|
||||
}
|
||||
//if (NodeModels.Count == 0)
|
||||
//{
|
||||
// return true; // 当前无节点,可以直接删除
|
||||
//}
|
||||
|
||||
if (MethodDetailsOfLibraryInfos.TryGetValue(library, out var mds)) // 存在方法
|
||||
{
|
||||
foreach (var md in mds)
|
||||
{
|
||||
if (groupedNodes.TryGetValue(md.MethodName, out int count))
|
||||
{
|
||||
if (count > 0)
|
||||
{
|
||||
return false; // 创建过相关的节点,无法移除
|
||||
}
|
||||
}
|
||||
}
|
||||
// 开始移除相关信息
|
||||
foreach (var md in mds)
|
||||
{
|
||||
MethodDetailss.TryRemove(md.MethodName, out _);
|
||||
}
|
||||
MethodDetailsOfLibraryInfos.TryRemove(library, out _);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return true;
|
||||
}
|
||||
//if (MethodDetailsOfLibraryInfos.TryGetValue(library, out var mds)) // 存在方法
|
||||
//{
|
||||
// foreach (var md in mds)
|
||||
// {
|
||||
// if (groupedNodes.TryGetValue(md.MethodName, out int count))
|
||||
// {
|
||||
// if (count > 0)
|
||||
// {
|
||||
// return false; // 创建过相关的节点,无法移除
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
// // 开始移除相关信息
|
||||
// foreach (var md in mds)
|
||||
// {
|
||||
// MethodDetailss.TryRemove(md.MethodName, out _);
|
||||
// }
|
||||
// MethodDetailsOfLibraryInfos.TryRemove(library, out _);
|
||||
// return true;
|
||||
//}
|
||||
//else
|
||||
//{
|
||||
// return true;
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
@@ -814,7 +800,9 @@ namespace Serein.NodeFlow.Env
|
||||
}
|
||||
else
|
||||
{
|
||||
if (MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails))
|
||||
if (FlowLibraryManagement.TryGetMethodDetails(methodDetailsInfo.AssemblyName,
|
||||
methodDetailsInfo.MethodName,
|
||||
out var methodDetails))
|
||||
{
|
||||
nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 一般的加载节点方法
|
||||
}
|
||||
@@ -1026,25 +1014,18 @@ namespace Serein.NodeFlow.Env
|
||||
/// 获取方法描述
|
||||
/// </summary>
|
||||
|
||||
public bool TryGetMethodDetailsInfo(string name, out MethodDetailsInfo? md)
|
||||
public bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo? mdInfo)
|
||||
{
|
||||
if (!string.IsNullOrEmpty(name))
|
||||
var isPass = FlowLibraryManagement.TryGetMethodDetails(assemblyName, methodName, out var md);
|
||||
if (!isPass || md is null)
|
||||
{
|
||||
foreach (var t_md in MethodDetailss.Values)
|
||||
{
|
||||
md = t_md.ToInfo();
|
||||
if (md != null)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
md = null;
|
||||
mdInfo = null;
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
md = null;
|
||||
return false;
|
||||
mdInfo = md?.ToInfo();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1058,18 +1039,9 @@ namespace Serein.NodeFlow.Env
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="delegateDetails"></param>
|
||||
/// <returns></returns>
|
||||
public bool TryGetDelegateDetails(string methodName, out DelegateDetails? delegateDetails)
|
||||
public bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails? delegateDetails)
|
||||
{
|
||||
|
||||
if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out delegateDetails))
|
||||
{
|
||||
return delegateDetails != null;
|
||||
}
|
||||
else
|
||||
{
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
}
|
||||
return FlowLibraryManagement.TryGetDelegateDetails(assemblyName, methodName, out delegateDetails);
|
||||
}
|
||||
|
||||
|
||||
@@ -1399,6 +1371,8 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
#region 私有方法
|
||||
|
||||
#region 暂时注释
|
||||
/*
|
||||
/// <summary>
|
||||
/// 加载指定路径的DLL文件
|
||||
/// </summary>
|
||||
@@ -1455,7 +1429,8 @@ namespace Serein.NodeFlow.Env
|
||||
|
||||
|
||||
|
||||
}
|
||||
}*/
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 移除连接关系
|
||||
@@ -1507,7 +1482,8 @@ namespace Serein.NodeFlow.Env
|
||||
return true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
#region 暂时注释
|
||||
/*/// <summary>
|
||||
/// 动态加载程序集
|
||||
/// </summary>
|
||||
/// <param name="assembly">程序集本身</param>
|
||||
@@ -1597,7 +1573,8 @@ namespace Serein.NodeFlow.Env
|
||||
Console.WriteLine(ex.ToString());
|
||||
return ([], []);
|
||||
}
|
||||
}
|
||||
}*/
|
||||
#endregion
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点
|
||||
|
||||
@@ -390,14 +390,14 @@ namespace Serein.NodeFlow.Env
|
||||
currentFlowEnvironment.TriggerInterrupt(nodeGuid, expression, type);
|
||||
}
|
||||
|
||||
public bool TryGetDelegateDetails(string methodName, out DelegateDetails del)
|
||||
public bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del)
|
||||
{
|
||||
return currentFlowEnvironment.TryGetDelegateDetails(methodName, out del);
|
||||
return currentFlowEnvironment.TryGetDelegateDetails(libraryName, methodName, out del);
|
||||
}
|
||||
|
||||
public bool TryGetMethodDetailsInfo(string methodName, out MethodDetailsInfo mdInfo)
|
||||
public bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo)
|
||||
{
|
||||
return currentFlowEnvironment.TryGetMethodDetailsInfo(methodName, out mdInfo);
|
||||
return currentFlowEnvironment.TryGetMethodDetailsInfo(libraryName, methodName, out mdInfo);
|
||||
}
|
||||
|
||||
public void WriteLineObjToJson(object obj)
|
||||
|
||||
@@ -89,13 +89,11 @@ namespace Serein.NodeFlow.Env
|
||||
/// </summary>
|
||||
/// <param name="libraryInfo"></param>
|
||||
/// <returns></returns>
|
||||
public static Library.Library ToLibrary(this Library.NodeLibraryInfo libraryInfo)
|
||||
public static NodeLibraryInfo ToLibrary(this Library.NodeLibraryInfo libraryInfo)
|
||||
{
|
||||
//var tmp = library.Assembly.ManifestModule.Name;
|
||||
return new Library.Library
|
||||
return new NodeLibraryInfo
|
||||
{
|
||||
AssemblyName = libraryInfo.AssemblyName,
|
||||
//AssemblyName = library.Assembly.GetName().Name,
|
||||
FileName = libraryInfo.FileName,
|
||||
FilePath = libraryInfo.FilePath,
|
||||
};
|
||||
|
||||
@@ -129,6 +129,7 @@ namespace Serein.NodeFlow.Env
|
||||
{
|
||||
AssemblyName = lib.AssemblyName,
|
||||
FilePath = "Remote",
|
||||
FileName = "Remote",
|
||||
};
|
||||
var mdInfos = lib.Mds.ToList();
|
||||
UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibraryInfo, mdInfos))); // 通知UI创建dll面板显示
|
||||
@@ -791,14 +792,14 @@ namespace Serein.NodeFlow.Env
|
||||
return ChannelFlowInterrupt.CancelType.Error;
|
||||
}
|
||||
|
||||
public bool TryGetMethodDetailsInfo(string methodName, out MethodDetailsInfo mdInfo)
|
||||
public bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:TryGetMethodDetailsInfo");
|
||||
mdInfo = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
public bool TryGetDelegateDetails(string methodName, out DelegateDetails del)
|
||||
public bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del)
|
||||
{
|
||||
Console.WriteLine("远程环境尚未实现的接口:TryGetDelegateDetails");
|
||||
del = null;
|
||||
|
||||
@@ -184,7 +184,7 @@ namespace Serein.NodeFlow
|
||||
//object?[]? args = [Context];
|
||||
foreach (var md in initMethods) // 初始化
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
@@ -205,7 +205,7 @@ namespace Serein.NodeFlow
|
||||
{
|
||||
//object?[]? data = [md.ActingInstance, args];
|
||||
//md.MethodDelegate.DynamicInvoke(data);
|
||||
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
@@ -228,7 +228,7 @@ namespace Serein.NodeFlow
|
||||
|
||||
foreach (MethodDetails? md in exitMethods)
|
||||
{
|
||||
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
if (!env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
|
||||
@@ -111,7 +111,8 @@ namespace Serein.NodeFlow.Model
|
||||
return new NodeInfo
|
||||
{
|
||||
Guid = Guid,
|
||||
MethodName = MethodDetails?.MethodName,
|
||||
AssemblyName = MethodDetails.AssemblyName,
|
||||
MethodName = MethodDetails.MethodName,
|
||||
Label = MethodDetails?.MethodAnotherName,
|
||||
Type = this.GetType().ToString(),
|
||||
TrueNodes = trueNodes.ToArray(),
|
||||
|
||||
@@ -43,7 +43,7 @@ namespace Serein.NodeFlow.Model
|
||||
#endregion
|
||||
|
||||
MethodDetails md = MethodDetails;
|
||||
if (!context.Env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception("不存在对应委托");
|
||||
}
|
||||
|
||||
230
NodeFlow/Tool/FlowLibrary.cs
Normal file
230
NodeFlow/Tool/FlowLibrary.cs
Normal file
@@ -0,0 +1,230 @@
|
||||
using Serein.Library;
|
||||
using Serein.NodeFlow.Tool;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.NodeFlow
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
///
|
||||
/// </summary>
|
||||
public class FlowLibrary
|
||||
{
|
||||
private readonly Assembly assembly;
|
||||
private readonly Action actionOfUnloadAssmbly;
|
||||
private string _assemblyFilePath;
|
||||
|
||||
public FlowLibrary(string assemblyFilePath,
|
||||
Assembly assembly,
|
||||
Action actionOfUnloadAssmbly)
|
||||
{
|
||||
this._assemblyFilePath = assemblyFilePath;
|
||||
this.assembly = assembly;
|
||||
this.actionOfUnloadAssmbly = actionOfUnloadAssmbly;
|
||||
LoadAssembly(assembly);
|
||||
}
|
||||
|
||||
|
||||
public string FullName => assembly.GetName().FullName;
|
||||
|
||||
public string Version => assembly.GetName().Version.ToString();
|
||||
|
||||
/// <summary>
|
||||
/// 加载程序集时创建的方法描述
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, MethodDetails> MethodDetailss { get; } = new ConcurrentDictionary<string, MethodDetails>();
|
||||
|
||||
/// <summary>
|
||||
/// 管理通过Emit动态构建的委托
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<string, DelegateDetails> DelegateDetailss { get; } = new ConcurrentDictionary<string, DelegateDetails>();
|
||||
|
||||
/// <summary>
|
||||
/// 记录不同的注册时机需要自动创建全局唯一实例的类型信息
|
||||
/// </summary>
|
||||
public ConcurrentDictionary<RegisterSequence, List<Type>> RegisterTypes { get; } = new ConcurrentDictionary<RegisterSequence, List<Type>>();
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 卸载当前程序集以及附带的所有信息
|
||||
/// </summary>
|
||||
public void Upload()
|
||||
{
|
||||
actionOfUnloadAssmbly?.Invoke();
|
||||
}
|
||||
|
||||
public NodeLibraryInfo ToInfo()
|
||||
{
|
||||
return new NodeLibraryInfo
|
||||
{
|
||||
AssemblyName = FullName,
|
||||
FileName = Path.GetFileName(_assemblyFilePath),
|
||||
FilePath = _assemblyFilePath,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
//public void LoadAssmely(Assembly assembly)
|
||||
//{
|
||||
// (var registerTypes, var mdlist) = LoadAssembly2(assembly);
|
||||
// if (mdlist.Count > 0)
|
||||
// {
|
||||
// var nodeLibraryInfo = new NodeLibraryInfo
|
||||
// {
|
||||
// //Assembly = assembly,
|
||||
// AssemblyName = assembly.FullName,
|
||||
// FileName = Path.GetFileName(_assemblyFilePath),
|
||||
// FilePath = _assemblyFilePath,
|
||||
// };
|
||||
|
||||
// //LibraryInfos.TryAdd(nodeLibraryInfo.AssemblyName, nodeLibraryInfo);
|
||||
|
||||
// MethodDetailss.TryAdd(nodeLibraryInfo.AssemblyName, mdlist);
|
||||
|
||||
// foreach (var md in mdlist)
|
||||
// {
|
||||
// MethodDetailss.TryAdd(md.MethodName, md);
|
||||
// }
|
||||
|
||||
// foreach (var kv in registerTypes)
|
||||
// {
|
||||
// if (!RegisterTypes.TryGetValue(kv.Key, out var types))
|
||||
// {
|
||||
// types = new List<Type>();
|
||||
// RegisterTypes.TryAdd(kv.Key, types);
|
||||
// }
|
||||
// types.AddRange(kv.Value);
|
||||
// }
|
||||
// var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 动态加载程序集
|
||||
/// </summary>
|
||||
/// <param name="assembly">程序集本身</param>
|
||||
/// <returns></returns>
|
||||
private bool LoadAssembly(Assembly assembly)
|
||||
{
|
||||
#region 检查入参
|
||||
|
||||
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
||||
var assemblyName = assembly.GetName().Name;
|
||||
if (string.IsNullOrEmpty(assemblyName)) // 防止动态程序集没有定义程序集名称
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
List<Type> types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型
|
||||
if (types.Count < 0) // 防止动态程序集中没有类型信息?
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
try
|
||||
{
|
||||
|
||||
|
||||
#region 获取 DynamicFlow 特性的流程控制器,如果没有退出
|
||||
// Type : 具有 DynamicFlowAttribute 标记的类型
|
||||
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性 (用于生成方法描述时,添加在方法别名中提高可读性)
|
||||
List<(Type Type, string Name)> scanTypes = new List<(Type Type, string Name)>();
|
||||
|
||||
// (Type, string)
|
||||
// Type : 具有 DynamicFlowAttribute 标记的类型
|
||||
// string : 类型元数据 DynamicFlowAttribute 特性中的 Name 属性
|
||||
foreach (var type in types)
|
||||
{
|
||||
if (type.GetCustomAttribute<DynamicFlowAttribute>() is DynamicFlowAttribute dynamicFlowAttribute && dynamicFlowAttribute.Scan == true)
|
||||
{
|
||||
scanTypes.Add((type, dynamicFlowAttribute.Name));
|
||||
}
|
||||
}
|
||||
if (scanTypes.Count == 0)
|
||||
{
|
||||
// 类型没有流程控制器
|
||||
return false;
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
#region 创建对应的方法元数据
|
||||
// 从 scanTypes.Type 创建的方法信息
|
||||
// Md : 方法描述
|
||||
// Dd :方法对应的Emit委托
|
||||
List<(MethodDetails Md, DelegateDetails Dd)> detailss = new List<(MethodDetails Md, DelegateDetails Dd)>();
|
||||
|
||||
// 遍历扫描的类型
|
||||
foreach ((var type, var flowName) in scanTypes)
|
||||
{
|
||||
var methodInfos = NodeMethodDetailsHelper.GetMethodsToProcess(type);
|
||||
foreach (var methodInfo in methodInfos) // 遍历流程控制器类型中的方法信息
|
||||
{
|
||||
// 尝试创建
|
||||
if (!NodeMethodDetailsHelper.TryCreateDetails(type, methodInfo, assemblyName,
|
||||
out var md, out var dd)) // 返回的描述
|
||||
{
|
||||
Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{methodInfo}");
|
||||
continue;
|
||||
}
|
||||
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
|
||||
detailss.Add((md, dd));
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 检查是否成功加载,如果成功,则真正写入到缓存的集合中
|
||||
if(detailss.Count == 0)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
#region 加载成功,缓存所有方法、委托的信息
|
||||
foreach((var md,var dd) in detailss)
|
||||
{
|
||||
MethodDetailss.TryAdd(md.MethodName, md);
|
||||
DelegateDetailss.TryAdd(md.MethodName, dd);
|
||||
}
|
||||
|
||||
#endregion
|
||||
#region 加载成功,开始获取并记录所有需要自动实例化的类型(在流程启动时)
|
||||
foreach (Type type in types)
|
||||
{
|
||||
if (type.GetCustomAttribute<AutoRegisterAttribute>() is AutoRegisterAttribute attribute)
|
||||
{
|
||||
if (!RegisterTypes.TryGetValue(attribute.Class, out var valus))
|
||||
{
|
||||
valus = new List<Type>();
|
||||
RegisterTypes.TryAdd(attribute.Class, valus);
|
||||
}
|
||||
valus.Add(type);
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#endregion
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine(ex.ToString());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,61 +0,0 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Runtime.Loader;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理加载在流程的程序集
|
||||
/// </summary>
|
||||
public class FlowLibraryLoader : AssemblyLoadContext
|
||||
{
|
||||
private Assembly _pluginAssembly;
|
||||
|
||||
public string FullName => _pluginAssembly.FullName;
|
||||
|
||||
/// <summary>
|
||||
/// 加载程序集
|
||||
/// </summary>
|
||||
/// <param name="pluginPath"></param>
|
||||
public FlowLibraryLoader(string pluginPath) : base(isCollectible: true)
|
||||
{
|
||||
_pluginAssembly = LoadFromAssemblyPath(pluginPath);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保持默认加载行为
|
||||
/// </summary>
|
||||
/// <param name="assemblyName"></param>
|
||||
/// <returns></returns>
|
||||
|
||||
protected override Assembly Load(AssemblyName assemblyName)
|
||||
{
|
||||
return null; // 保持默认加载行为
|
||||
}
|
||||
|
||||
|
||||
public List<Type> LoadFlowTypes()
|
||||
{
|
||||
return _pluginAssembly.GetTypes().ToList();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否对程序集的引用
|
||||
/// </summary>
|
||||
public void UnloadPlugin()
|
||||
{
|
||||
_pluginAssembly = null; // 释放对程序集的引用
|
||||
Unload(); // 触发卸载
|
||||
// 强制进行垃圾回收,以便完成卸载
|
||||
GC.Collect();
|
||||
GC.WaitForPendingFinalizers();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
264
NodeFlow/Tool/FlowLibraryManagement.cs
Normal file
264
NodeFlow/Tool/FlowLibraryManagement.cs
Normal file
@@ -0,0 +1,264 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.FlowNode;
|
||||
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;
|
||||
|
||||
namespace Serein.NodeFlow.Tool
|
||||
{
|
||||
/// <summary>
|
||||
/// 管理加载在运行环境中的外部程序集
|
||||
/// </summary>
|
||||
public class FlowLibraryManagement
|
||||
{
|
||||
public FlowLibraryManagement(IFlowEnvironment flowEnvironment)
|
||||
{
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
}
|
||||
|
||||
private readonly IFlowEnvironment flowEnvironment;
|
||||
|
||||
/// <summary>
|
||||
/// 缓存所有加载了的程序集
|
||||
/// </summary>
|
||||
private ConcurrentDictionary<string, FlowLibrary> _myFlowLibrarys = new ConcurrentDictionary<string, FlowLibrary>();
|
||||
|
||||
public (NodeLibraryInfo,List<MethodDetailsInfo>) LoadLibrary(string libraryfilePath)
|
||||
{
|
||||
return LoadDllNodeInfo(libraryfilePath);
|
||||
}
|
||||
|
||||
public bool UnloadLibrary(string libraryName)
|
||||
{
|
||||
if (_myFlowLibrarys.TryGetValue(libraryName, out var flowLibrary))
|
||||
{
|
||||
try
|
||||
{
|
||||
flowLibrary.Upload();
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"尝试卸载程序集[{libraryName}]发生错误:{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<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>
|
||||
/// <param name="dllPath"></param>
|
||||
/// <returns></returns>
|
||||
private (NodeLibraryInfo, List<MethodDetailsInfo>) LoadDllNodeInfo(string dllPath)
|
||||
{
|
||||
|
||||
|
||||
var fileName = Path.GetFileName(dllPath); // 获取文件名
|
||||
Assembly assembly = Assembly.LoadFrom(dllPath); // 加载程序集
|
||||
FlowLibrary flowLibrary = new FlowLibrary(dllPath, assembly, () =>
|
||||
{
|
||||
Console.WriteLine("暂未实现卸载程序集");
|
||||
//flowAlc.Unload(); // 卸载程序集
|
||||
//flowAlc = null;
|
||||
//GC.Collect(); // 强制触发GC确保卸载成功
|
||||
//GC.WaitForPendingFinalizers();
|
||||
});
|
||||
|
||||
_myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary);
|
||||
|
||||
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(),
|
||||
flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList());
|
||||
return result;
|
||||
|
||||
|
||||
#if false
|
||||
var fileName = Path.GetFileName(dllPath); // 获取文件名
|
||||
var flowAlc = new AssemblyLoadContext(fileName, true);
|
||||
flowAlc.LoadFromAssemblyPath(dllPath); // 加载指定路径的程序集
|
||||
flowAlc.LoadFromAssemblyPath(@"F:\临时\project\yolo flow\OpenCvSharp.dll"); // 加载指定路径的程序集
|
||||
|
||||
var assemblt = flowAlc.Assemblies.ToArray()[0]; // 目前只会加载一个程序集,所以这样获取
|
||||
FlowLibrary flowLibrary = new FlowLibrary(dllPath, assemblt, () =>
|
||||
{
|
||||
flowAlc.Unload(); // 卸载程序集
|
||||
flowAlc = null;
|
||||
GC.Collect(); // 强制触发GC确保卸载成功
|
||||
GC.WaitForPendingFinalizers();
|
||||
});
|
||||
_myFlowLibrarys.TryAdd(assemblt.GetName().Name, flowLibrary);
|
||||
return flowLibrary.ToInfo();
|
||||
|
||||
|
||||
//foreach (var assemblt in flowAlc.Assemblies)
|
||||
//{
|
||||
// FlowLibrary flowLibrary = new FlowLibrary(dllPath, assemblt, () =>
|
||||
// {
|
||||
// flowAlc.Unload(); // 卸载程序集
|
||||
// flowAlc = null;
|
||||
// GC.Collect(); // 强制触发GC确保卸载成功
|
||||
// GC.WaitForPendingFinalizers();
|
||||
// });
|
||||
//}
|
||||
|
||||
#endif
|
||||
//if (OperatingSystem.IsWindows())
|
||||
//{
|
||||
// UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibraryInfo, mdInfos))); // 通知UI创建dll面板显示
|
||||
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
|
||||
|
||||
///// <summary>
|
||||
///// 是否对程序集的引用
|
||||
///// </summary>
|
||||
//public void UnloadPlugin()
|
||||
//{
|
||||
// _pluginAssembly = null; // 释放对程序集的引用
|
||||
// Unload(); // 触发卸载
|
||||
// // 强制进行垃圾回收,以便完成卸载
|
||||
// GC.Collect();
|
||||
// GC.WaitForPendingFinalizers();
|
||||
//}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
@@ -5,6 +5,7 @@ using System.Collections.Concurrent;
|
||||
using System.Reflection;
|
||||
using Serein.Library.FlowNode;
|
||||
using System.Diagnostics;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
namespace Serein.NodeFlow.Tool;
|
||||
|
||||
@@ -21,21 +22,35 @@ public static class NodeMethodDetailsHelper
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 创建方法信息
|
||||
/// 创建方法信息/委托信息
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
public static (MethodDetails?, DelegateDetails?) CreateMethodDetails(Type type, MethodInfo method, string assemblyName)
|
||||
/// <param name="type">方法所属的类型</param>
|
||||
/// <param name="methodInfo">方法信息</param>
|
||||
/// <param name="assemblyName">方法所属的程序集名称</param>
|
||||
/// <param name="methodDetails">创建的方法描述,用来生成节点信息</param>
|
||||
/// <param name="delegateDetails">方法对应的Emit动态委托</param>
|
||||
/// <returns>指示是否创建成功</returns>
|
||||
public static bool TryCreateDetails(Type type,
|
||||
MethodInfo methodInfo,
|
||||
string assemblyName,
|
||||
[MaybeNullWhen(false)] out MethodDetails methodDetails,
|
||||
[MaybeNullWhen(false)] out DelegateDetails delegateDetails)
|
||||
{
|
||||
var attribute = method.GetCustomAttribute<NodeActionAttribute>();
|
||||
|
||||
|
||||
var attribute = methodInfo.GetCustomAttribute<NodeActionAttribute>();
|
||||
if(attribute is null || attribute.Scan == false)
|
||||
{
|
||||
return (null, null);
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
}
|
||||
//var dllTypeName = $"{assemblyName}.{type.Name}";
|
||||
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
||||
Console.WriteLine("loading method : " +dllTypeMethodName);
|
||||
Debug.WriteLine("loading method : " +dllTypeMethodName);
|
||||
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
||||
|
||||
var methodName = $"{assemblyName}.{type.Name}.{methodInfo.Name}";
|
||||
Console.WriteLine("loading method : " + methodName);
|
||||
|
||||
// 创建参数信息
|
||||
var explicitDataOfParameters = GetExplicitDataOfParameters(methodInfo.GetParameters());
|
||||
|
||||
|
||||
|
||||
@@ -46,17 +61,17 @@ public static class NodeMethodDetailsHelper
|
||||
// method.ReturnType);// 返回值
|
||||
|
||||
//// 通过表达式树生成委托
|
||||
var emitMethodType = EmitHelper.CreateDynamicMethod(method, out var methodDelegate);// 返回值
|
||||
var emitMethodType = EmitHelper.CreateDynamicMethod(methodInfo, out var methodDelegate);// 返回值
|
||||
|
||||
Type? returnType;
|
||||
bool isTask = IsGenericTask(method.ReturnType, out var taskResult);
|
||||
bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult);
|
||||
|
||||
if (attribute.MethodDynamicType == Library.NodeType.Flipflop)
|
||||
{
|
||||
if (method.ReturnType.IsGenericType && method.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
|
||||
{
|
||||
// 获取 Task<> 的泛型参数类型
|
||||
var innerType = method.ReturnType.GetGenericArguments()[0];
|
||||
var innerType = methodInfo.ReturnType.GetGenericArguments()[0];
|
||||
if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(IFlipflopContext<>))
|
||||
{
|
||||
var flipflopType = innerType.GetGenericArguments()[0];
|
||||
@@ -64,14 +79,18 @@ public static class NodeMethodDetailsHelper
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[{dllTypeMethodName}]跳过创建,返回类型非预期的Task<IFlipflopContext<TResult>>。");
|
||||
return (null, null);
|
||||
Console.WriteLine($"[{methodName}]跳过创建,返回类型非预期的Task<IFlipflopContext<TResult>>。");
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"[{dllTypeMethodName}]跳过创建,因为触发器方法的返回值并非Task<>,将无法等待。");
|
||||
return (null, null);
|
||||
Console.WriteLine($"[{methodName}]跳过创建,因为触发器方法的返回值并非Task<>,将无法等待。");
|
||||
methodDetails = null;
|
||||
delegateDetails = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
//if (!isTask || taskResult != typeof(IFlipflopContext<object>))
|
||||
@@ -86,11 +105,11 @@ public static class NodeMethodDetailsHelper
|
||||
}
|
||||
else
|
||||
{
|
||||
returnType = method.ReturnType;
|
||||
returnType = methodInfo.ReturnType;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(attribute.AnotherName)){
|
||||
attribute.AnotherName = method.Name;
|
||||
attribute.AnotherName = methodInfo.Name;
|
||||
}
|
||||
|
||||
|
||||
@@ -107,18 +126,22 @@ public static class NodeMethodDetailsHelper
|
||||
var md = new MethodDetails() // 从DLL生成方法描述(元数据)
|
||||
{
|
||||
ActingInstanceType = type,
|
||||
// ActingInstance = instance,
|
||||
MethodName = dllTypeMethodName,
|
||||
// ActingInstance = instance,
|
||||
MethodName = methodName,
|
||||
AssemblyName = assemblyName,
|
||||
MethodDynamicType = attribute.MethodDynamicType,
|
||||
MethodLockName = attribute.LockName,
|
||||
MethodAnotherName = methodMethodAnotherName,
|
||||
ParameterDetailss = explicitDataOfParameters,
|
||||
ReturnType = returnType,
|
||||
// 如果存在可变参数,取最后一个元素的下标,否则为-1;
|
||||
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length-1 : -1,
|
||||
ParamsArgIndex = hasParamsArg ? explicitDataOfParameters.Length - 1 : -1,
|
||||
};
|
||||
var dd = new DelegateDetails(emitMethodType, methodDelegate) ;
|
||||
return (md, dd);
|
||||
|
||||
methodDetails = md;
|
||||
delegateDetails = dd;
|
||||
return true;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -2505,7 +2505,7 @@ namespace Serein.Workbench
|
||||
Console.WriteLine(savePath);
|
||||
for (int index = 0; index < projectData.Librarys.Length; index++)
|
||||
{
|
||||
Library.Library? library = projectData.Librarys[index];
|
||||
NodeLibraryInfo? library = projectData.Librarys[index];
|
||||
try
|
||||
{
|
||||
string targetPath = System.IO.Path.Combine(librarySavePath, library.FileName); // 目标文件夹
|
||||
|
||||
Reference in New Issue
Block a user