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

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

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