改写了流程依赖管理,封装为一个工具类,将来计划实现动态增加卸载/更新类库的功能

This commit is contained in:
fengjiayi
2024-11-03 21:17:45 +08:00
parent a76091092d
commit e4972c62f2
17 changed files with 700 additions and 337 deletions

View File

@@ -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

View File

@@ -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
{
}
}
}
}

View File

@@ -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;
}

View File

@@ -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}不存在对应委托");
}

View File

@@ -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; }

View File

@@ -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>
/// 创建节点

View File

@@ -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)

View File

@@ -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,
};

View File

@@ -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;

View File

@@ -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("不存在对应委托");
}

View File

@@ -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(),

View File

@@ -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("不存在对应委托");
}

View 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;
}
}
}
}

View File

@@ -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();
}
}
}

View 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();
//}
}
}

View File

@@ -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;
}

View File

@@ -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); // 目标文件夹