从DLL导入方法将按照方法名称首字母开始排序;规范化方法参数描述中的输入类型,改为枚举。

This commit is contained in:
fengjiayi
2025-03-15 15:43:42 +08:00
parent d8f4a5a2c2
commit 1ae4c87aac
14 changed files with 165 additions and 57 deletions

View File

@@ -0,0 +1,14 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace Serein.Library
{
public enum ParameterValueInputType
{
Input,
Select,
}
}

View File

@@ -74,7 +74,7 @@ namespace Serein.Library
pd.DataType = null; pd.DataType = null;
pd.Name = null; pd.Name = null;
pd.ArgDataSourceNodeGuid = null; pd.ArgDataSourceNodeGuid = null;
pd.ExplicitTypeName = null; pd.InputType = ParameterValueInputType.Input;
} }
this.MethodDetails.ParameterDetailss = null; this.MethodDetails.ParameterDetailss = null;
this.MethodDetails.ActingInstance = null; this.MethodDetails.ActingInstance = null;

View File

@@ -10,6 +10,8 @@ using System.Threading.Tasks;
namespace Serein.Library namespace Serein.Library
{ {
/// <summary> /// <summary>
/// 节点入参参数详情 /// 节点入参参数详情
/// </summary> /// </summary>
@@ -57,7 +59,7 @@ namespace Serein.Library
/// <para>Value :除以上类型之外的任意参数</para> /// <para>Value :除以上类型之外的任意参数</para>
/// </summary> /// </summary>
[PropertyInfo] [PropertyInfo]
private string _explicitTypeName ; private ParameterValueInputType _inputType ;
/// <summary> /// <summary>
/// 入参数据来源。默认使用上一节点作为入参数据。 /// 入参数据来源。默认使用上一节点作为入参数据。
@@ -91,7 +93,7 @@ namespace Serein.Library
private string _dataValue; private string _dataValue;
/// <summary> /// <summary>
/// 只有当ExplicitTypeName 为 Select 时,才会需要该成员。 /// 只有当 InputType 为 Select 时,才会需要该成员。
/// </summary> /// </summary>
[PropertyInfo(IsNotification = true)] [PropertyInfo(IsNotification = true)]
private string[] _items ; private string[] _items ;
@@ -104,6 +106,8 @@ namespace Serein.Library
} }
public partial class ParameterDetails public partial class ParameterDetails
{ {
@@ -133,7 +137,7 @@ namespace Serein.Library
Name = info.Name; Name = info.Name;
DataType = Type.GetType(info.DataTypeFullName); DataType = Type.GetType(info.DataTypeFullName);
ExplicitType = Type.GetType(info.ExplicitTypeFullName); ExplicitType = Type.GetType(info.ExplicitTypeFullName);
ExplicitTypeName = info.ExplicitTypeName; InputType = info.InputType.ConvertEnum<ParameterValueInputType>();
Items = info.Items; Items = info.Items;
IsParams = info.IsParams; IsParams = info.IsParams;
} }
@@ -151,7 +155,7 @@ namespace Serein.Library
DataTypeFullName = this.DataType.FullName, DataTypeFullName = this.DataType.FullName,
Name = this.Name, Name = this.Name,
ExplicitTypeFullName = this.ExplicitType.FullName, ExplicitTypeFullName = this.ExplicitType.FullName,
ExplicitTypeName = this.ExplicitTypeName, InputType = this.InputType.ToString(),
Items = this.Items.Select(it => it).ToArray(), Items = this.Items.Select(it => it).ToArray(),
}; };
} }
@@ -168,7 +172,7 @@ namespace Serein.Library
Index = this.Index, Index = this.Index,
IsExplicitData = this.IsExplicitData, IsExplicitData = this.IsExplicitData,
ExplicitType = this.ExplicitType, ExplicitType = this.ExplicitType,
ExplicitTypeName = this.ExplicitTypeName, InputType = this.InputType,
//Convertor = this.Convertor, //Convertor = this.Convertor,
DataType = this.DataType, DataType = this.DataType,
Name = this.Name, Name = this.Name,
@@ -199,6 +203,11 @@ namespace Serein.Library
{ {
return context; return context;
} }
// 返回流程上下文
if (typeof(NodeModelBase).IsAssignableFrom(DataType))
{
return NodeModel;
}
// 显式设置的参数 // 显式设置的参数
if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase)) if (IsExplicitData && !DataValue.StartsWith("@", StringComparison.OrdinalIgnoreCase))
{ {

View File

@@ -43,7 +43,7 @@ namespace Serein.Library
/// <para>Bool : 布尔类型</para> /// <para>Bool : 布尔类型</para>
/// <para>Value 除以上类型之外的任意参数</para> /// <para>Value 除以上类型之外的任意参数</para>
/// </summary> /// </summary>
public string ExplicitTypeName { get; set; } public string InputType { get; set; }
/// <summary> /// <summary>
/// 参数选择器 /// 参数选择器

View File

@@ -71,7 +71,7 @@ namespace Serein.NodeFlow.Model
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this, NodeModel = this,
//Convertor = null, //Convertor = null,
ExplicitTypeName = "Value", InputType = ParameterValueInputType.Input,
Items = null, Items = null,
}; };
this.MethodDetails.ParameterDetailss = [..pd]; this.MethodDetails.ParameterDetailss = [..pd];

View File

@@ -60,7 +60,7 @@ namespace Serein.NodeFlow.Model
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this, NodeModel = this,
//Convertor = null, //Convertor = null,
ExplicitTypeName = "Value", InputType = ParameterValueInputType.Input,
Items = null, Items = null,
}; };
this.MethodDetails.ParameterDetailss = [.. pd]; this.MethodDetails.ParameterDetailss = [.. pd];

View File

@@ -89,7 +89,7 @@ namespace Serein.NodeFlow.Model
ArgDataSourceNodeGuid = string.Empty, ArgDataSourceNodeGuid = string.Empty,
ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData, ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData,
NodeModel = this, NodeModel = this,
ExplicitTypeName = "Value", InputType = ParameterValueInputType.Input,
Items = null, Items = null,
IsParams = true, IsParams = true,
}; };

View File

@@ -5,13 +5,21 @@ using System;
using System.Collections.Concurrent; using System.Collections.Concurrent;
using System.Collections.Generic; using System.Collections.Generic;
using System.Diagnostics; using System.Diagnostics;
using System.Globalization;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using System.Text; using System.Text;
using static System.Runtime.InteropServices.JavaScript.JSType;
namespace Serein.NodeFlow namespace Serein.NodeFlow
{ {
public class LibraryMdDd (MethodDetails methodDetails,DelegateDetails delegateDetails)
{
public MethodDetails MethodDetails { get; } = methodDetails;
public DelegateDetails DelegateDetails { get; } = delegateDetails;
}
/// <summary> /// <summary>
/// 加载在流程中的程序集依赖 /// 加载在流程中的程序集依赖
@@ -111,7 +119,7 @@ namespace Serein.NodeFlow
var loaderExceptions = ex.LoaderExceptions; var loaderExceptions = ex.LoaderExceptions;
foreach (var loaderException in loaderExceptions) foreach (var loaderException in loaderExceptions)
{ {
SereinEnv.WriteLine(InfoType.ERROR, loaderException.Message); SereinEnv.WriteLine(InfoType.ERROR, loaderException?.Message);
} }
return false; return false;
} }
@@ -149,7 +157,7 @@ namespace Serein.NodeFlow
// 从 scanTypes.Type 创建的方法信息 // 从 scanTypes.Type 创建的方法信息
// Md : 方法描述 // Md : 方法描述
// Dd 方法对应的Emit委托 // Dd 方法对应的Emit委托
List<(MethodDetails Md, DelegateDetails Dd)> detailss = new List<(MethodDetails Md, DelegateDetails Dd)>(); List<LibraryMdDd> detailss = new List<LibraryMdDd>();
// 遍历扫描的类型 // 遍历扫描的类型
foreach ((var type, var flowName) in scanTypes) foreach ((var type, var flowName) in scanTypes)
@@ -165,7 +173,7 @@ namespace Serein.NodeFlow
continue; continue;
} }
md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名 md.MethodAnotherName = flowName + md.MethodAnotherName; // 方法别名
detailss.Add((md, dd)); detailss.Add(new LibraryMdDd(md, dd));
} }
} }
@@ -176,11 +184,26 @@ namespace Serein.NodeFlow
{ {
return false; return false;
} }
#region // 简单排序一下
foreach ((var md, var dd) in detailss) //detailss = detailss.OrderBy(k => k.MethodDetails.MethodName,).ToList();
detailss.Sort((a, b) => string.Compare(a.MethodDetails.MethodName, b.MethodDetails.MethodName, StringComparison.OrdinalIgnoreCase));
foreach (var item in detailss)
{ {
MethodDetailss.TryAdd(md.MethodName, md); SereinEnv.WriteLine(InfoType.INFO, "loading method : " + item.MethodDetails.MethodName);
DelegateDetailss.TryAdd(md.MethodName, dd);
}
//detailss.Sort((a, b) => string.Compare());
#region
foreach (var item in detailss)
{
var key = item.MethodDetails.MethodName;
MethodDetailss.TryAdd(key, item.MethodDetails);
DelegateDetailss.TryAdd(key, item.DelegateDetails);
} }
#endregion #endregion

View File

@@ -229,7 +229,8 @@ namespace Serein.NodeFlow.Tool
GC.WaitForPendingFinalizers(); GC.WaitForPendingFinalizers();
}; };
var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集 var assembly = flowAlc.LoadFromAssemblyPath(dllFilePath); // 加载指定路径的程序集
return LoadAssembly(assembly, actionUnload); var assembly_result = LoadAssembly(assembly, actionUnload);
return assembly_result;
} }
/* var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径 /* var dir = Path.GetDirectoryName(dllFilePath); // 获取目录路径
@@ -272,15 +273,26 @@ namespace Serein.NodeFlow.Tool
} }
FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload); FlowLibrary flowLibrary = new FlowLibrary(assembly, actionUnload);
if (flowLibrary.LoadAssembly(assembly)) var loadResult = flowLibrary.LoadAssembly(assembly); // 加载程序集
if (loadResult)
{ {
_myFlowLibrarys.TryAdd(assembly.GetName().Name, flowLibrary); var assemblyName = assembly.GetName().Name;
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(), if (string.IsNullOrEmpty(assemblyName))
flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList()); {
actionUnload.Invoke();
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败,没有程序集名称");
}
_myFlowLibrarys.TryAdd(assemblyName, flowLibrary);
List<MethodDetailsInfo> mdInfos = flowLibrary.MethodDetailss.Values.Select(md => md.ToInfo()).ToList();
mdInfos.Sort((a, b) => string.Compare(a.MethodName, b.MethodName, StringComparison.OrdinalIgnoreCase));
(NodeLibraryInfo, List<MethodDetailsInfo>) result = (flowLibrary.ToInfo(), mdInfos);
return result; return result;
} }
else else
{ {
actionUnload.Invoke();
throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败"); throw new Exception($"程序集[{assembly.GetName().FullName}]加载失败");
} }
} }

View File

@@ -6,6 +6,7 @@ using System.Reflection;
using Serein.Library.FlowNode; using Serein.Library.FlowNode;
using System.Diagnostics; using System.Diagnostics;
using System.Diagnostics.CodeAnalysis; using System.Diagnostics.CodeAnalysis;
using System.ComponentModel.DataAnnotations;
namespace Serein.NodeFlow.Tool; namespace Serein.NodeFlow.Tool;
@@ -47,13 +48,10 @@ public static class NodeMethodDetailsHelper
} }
var methodName = $"{assemblyName}.{type.Name}.{methodInfo.Name}"; var methodName = $"{assemblyName}.{type.Name}.{methodInfo.Name}";
SereinEnv.WriteLine(InfoType.INFO, "loading method : " + methodName);
// 创建参数信息 // 创建参数信息
var explicitDataOfParameters = GetExplicitDataOfParameters(methodInfo.GetParameters()); var explicitDataOfParameters = GetExplicitDataOfParameters(methodInfo.GetParameters());
//// 通过表达式树生成委托 //// 通过表达式树生成委托
//var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型 //var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
// method, // 方法信息 // method, // 方法信息
@@ -65,6 +63,34 @@ public static class NodeMethodDetailsHelper
Type? returnType; Type? returnType;
bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult); bool isTask = IsGenericTask(methodInfo.ReturnType, out var taskResult);
if (attribute.MethodDynamicType == Library.NodeType.UI)
{
if (isTask)
{
var innerType = methodInfo.ReturnType.GetGenericArguments()[0];
if (innerType.IsGenericType && innerType != typeof(IEmbeddedContent))
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建因为UI方法的返回值并非IEmbeddedContent流程工作台将无法正确显示自定义控件界面以及传递数据。");
methodDetails = null;
delegateDetails = null;
return false;
}
}
else
{
if (methodInfo.ReturnType != typeof(IEmbeddedContent))
{
SereinEnv.WriteLine(InfoType.WARN, $"[{methodName}]跳过创建因为UI方法的返回值并非IEmbeddedContent流程工作台将无法正确显示自定义控件界面以及传递数据。");
methodDetails = null;
delegateDetails = null;
return false;
}
}
}
// 对于触发器
if (attribute.MethodDynamicType == Library.NodeType.Flipflop) if (attribute.MethodDynamicType == Library.NodeType.Flipflop)
{ {
if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>)) if (methodInfo.ReturnType.IsGenericType && methodInfo.ReturnType.GetGenericTypeDefinition() == typeof(Task<>))
@@ -91,13 +117,8 @@ public static class NodeMethodDetailsHelper
delegateDetails = null; delegateDetails = null;
return false; return false;
} }
//if (!isTask || taskResult != typeof(IFlipflopContext<object>))
//{
//
//}
} }
else if(isTask) else if(isTask)
{ {
returnType = taskResult is null ? typeof(Task) : taskResult; returnType = taskResult is null ? typeof(Task) : taskResult;
@@ -111,8 +132,6 @@ public static class NodeMethodDetailsHelper
attribute.AnotherName = methodInfo.Name; attribute.AnotherName = methodInfo.Name;
} }
var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ; var asyncPrefix = "[异步]"; // IsGenericTask(returnType) ? "[async]" : ;
var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName; var methodMethodAnotherName = isTask ? asyncPrefix + attribute.AnotherName : attribute.AnotherName;
@@ -126,7 +145,7 @@ public static class NodeMethodDetailsHelper
{ {
ActingInstanceType = type, ActingInstanceType = type,
// ActingInstance = instance, // ActingInstance = instance,
MethodName = methodName, MethodName = NodeMethodDetailsHelper.GetMethodSignature(methodInfo),
AssemblyName = assemblyName, AssemblyName = assemblyName,
MethodDynamicType = attribute.MethodDynamicType, MethodDynamicType = attribute.MethodDynamicType,
MethodLockName = attribute.LockName, MethodLockName = attribute.LockName,
@@ -147,7 +166,17 @@ public static class NodeMethodDetailsHelper
return true; return true;
} }
public static string GetMethodSignature(MethodInfo method)
{
if (method == null) return string.Empty;
string methodName = method.Name;
string returnType = method.ReturnType.Name;
string parameters = string.Join(", ", method.GetParameters()
.Select(p => $"{p.ParameterType.Name} {p.Name}"));
return $"{methodName}({parameters}) : {returnType}";
}
public static bool IsGenericTask(Type returnType, out Type? taskResult) public static bool IsGenericTask(Type returnType, out Type? taskResult)
{ {
// 判断是否为 Task 类型或泛型 Task<T> // 判断是否为 Task 类型或泛型 Task<T>
@@ -236,6 +265,27 @@ public static class NodeMethodDetailsHelper
#endregion #endregion
}).ToArray(); }).ToArray();
foreach(var pd in tempParams)
{
var argType = pd.DataType;
// 运行环境
if (typeof(IFlowEnvironment).IsAssignableFrom(argType))
{
continue;
}
// 流程上下文
if (typeof(IDynamicContext).IsAssignableFrom(argType))
{
continue;
}
// 节点模型
if (typeof(NodeModelBase).IsAssignableFrom(argType))
{
continue;
}
pd.IsExplicitData = true;
}
return tempParams; return tempParams;
} }
@@ -260,14 +310,14 @@ public static class NodeMethodDetailsHelper
dataType = parameterInfo.ParameterType; dataType = parameterInfo.ParameterType;
} }
string explicitTypeName = GetExplicitTypeName(explicitParemType); var inputType = GetInputType(explicitParemType);
var items = GetExplicitItems(explicitParemType, explicitTypeName); var items = GetExplicitItems(explicitParemType, inputType);
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型 //if ("Bool".Equals(inputType)) inputType = "Select"; // 布尔值 转为 可选类型
return new ParameterDetails return new ParameterDetails
{ {
IsExplicitData = isExplicitData, //attribute is null ? parameterInfo.HasDefaultValue : true, IsExplicitData = isExplicitData, //attribute is null ? parameterInfo.HasDefaultValue : true,
Index = index, // 索引 Index = index, // 索引
ExplicitTypeName = explicitTypeName, // Select/Bool/Value InputType = inputType, // Select/Bool/Value
ExplicitType = explicitParemType,// 显示的入参类型 ExplicitType = explicitParemType,// 显示的入参类型
//Convertor = func, // 转换器 //Convertor = func, // 转换器
DataType = dataType, // 实际的入参类型 DataType = dataType, // 实际的入参类型
@@ -286,17 +336,17 @@ public static class NodeMethodDetailsHelper
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
/// <returns></returns> /// <returns></returns>
private static string GetExplicitTypeName(Type type) private static ParameterValueInputType GetInputType(Type type)
{ {
return type switch return type switch
{ {
Type t when t.IsEnum => "Select", Type t when t.IsEnum => ParameterValueInputType.Select,
Type t when t == typeof(bool) => "Bool", Type t when t == typeof(bool) => ParameterValueInputType.Select,
Type t when t == typeof(string) => "Value", Type t when t == typeof(string) => ParameterValueInputType.Input,
Type t when t == typeof(int) => "Value", Type t when t == typeof(int) => ParameterValueInputType.Input,
Type t when t == typeof(double) => "Value", Type t when t == typeof(double) => ParameterValueInputType.Input,
_ => "Value" _ => ParameterValueInputType.Input
}; };
} }
@@ -305,14 +355,14 @@ public static class NodeMethodDetailsHelper
/// 获取参数列表选项 /// 获取参数列表选项
/// </summary> /// </summary>
/// <param name="type"></param> /// <param name="type"></param>
/// <param name="explicitTypeName"></param> /// <param name="InputType"></param>
/// <returns></returns> /// <returns></returns>
private static IEnumerable<string> GetExplicitItems(Type type, string explicitTypeName) private static IEnumerable<string> GetExplicitItems(Type type, ParameterValueInputType InputType)
{ {
IEnumerable<string> items = explicitTypeName switch IEnumerable<string> items = InputType switch
{ {
"Select" => Enum.GetNames(type), ParameterValueInputType.Select when type == typeof(bool) => ["True", "False"],
"Bool" => ["True", "False"], ParameterValueInputType.Select => Enum.GetNames(type),
_ => [] _ => []
}; };
return items; return items;

View File

@@ -18,7 +18,7 @@ namespace Serein.Workbench.Avalonia.Converters
if(value is ParameterDetails pd) if(value is ParameterDetails pd)
{ {
if (pd.ExplicitTypeName == "Value") if (pd.InputType == ParameterValueInputType.Input)
{ {
return false; return false;

View File

@@ -48,7 +48,7 @@ namespace Serein.Workbench.Avalonia.Custom.ViewModels
return; return;
} }
if ("Value".Equals(ParameterDetails.ExplicitTypeName)) if (ParameterDetails.InputType == ParameterValueInputType.Input )
{ {
// 值类型 // 值类型
IsVisibleA = false; IsVisibleA = false;

View File

@@ -72,7 +72,7 @@ namespace Serein.Workbench.Node.View
// 获取 MethodDetailsControl 实例 // 获取 MethodDetailsControl 实例
var methodDetailsControl = this.MethodDetailsControl; var methodDetailsControl = this.MethodDetailsControl;
var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl var itemsControl = FindVisualChild<ItemsControl>(methodDetailsControl); // 查找 ItemsControl
if (itemsControl != null) if (itemsControl != null && base.ViewModel.NodeModel.MethodDetails.ParameterDetailss != null)
{ {
var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length]; var argDataJunction = new JunctionControlBase[base.ViewModel.NodeModel.MethodDetails.ParameterDetailss.Length];
var controls = new List<JunctionControlBase>(); var controls = new List<JunctionControlBase>();

View File

@@ -137,7 +137,7 @@
<MultiDataTrigger> <MultiDataTrigger>
<MultiDataTrigger.Conditions> <MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsExplicitData}" Value="true" /> <Condition Binding="{Binding IsExplicitData}" Value="true" />
<Condition Binding="{Binding ExplicitTypeName}" Value="Select" /> <Condition Binding="{Binding InputType}" Value="Select" />
</MultiDataTrigger.Conditions> </MultiDataTrigger.Conditions>
<Setter Property="ContentTemplate"> <Setter Property="ContentTemplate">
<Setter.Value> <Setter.Value>
@@ -160,7 +160,7 @@
<MultiDataTrigger> <MultiDataTrigger>
<MultiDataTrigger.Conditions> <MultiDataTrigger.Conditions>
<Condition Binding="{Binding IsExplicitData}" Value="true" /> <Condition Binding="{Binding IsExplicitData}" Value="true" />
<Condition Binding="{Binding ExplicitTypeName}" Value="Value" /> <Condition Binding="{Binding InputType}" Value="Input" />
</MultiDataTrigger.Conditions> </MultiDataTrigger.Conditions>
<Setter Property="ContentTemplate"> <Setter Property="ContentTemplate">
<Setter.Value> <Setter.Value>