mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-02 06:26:35 +08:00
实现了拖拽式设置方法调用顺序、方法入参参数来源
This commit is contained in:
@@ -15,8 +15,16 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
IFlowEnvironment Env { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否正在运行
|
||||
/// </summary>
|
||||
RunState RunState { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 下一个要执行的节点
|
||||
/// </summary>
|
||||
ConnectionInvokeType NextOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 获取节点的数据(当前节点需要获取上一节点数据时,需要从 运行时上一节点 的Guid 通过这个方法进行获取
|
||||
/// </summary>
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
|
||||
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
@@ -175,12 +176,33 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
Remote,
|
||||
}
|
||||
public NodeConnectChangeEventArgs(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType, ConnectChangeType changeType)
|
||||
public NodeConnectChangeEventArgs(string fromNodeGuid,
|
||||
string toNodeGuid,
|
||||
JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
|
||||
ConnectionInvokeType connectionInvokeType, // 节点调用的方法类型(true/false/error/cancel )
|
||||
ConnectChangeType changeType) // 需要创建连接线还是删除连接线
|
||||
{
|
||||
this.FromNodeGuid = fromNodeGuid;
|
||||
this.ToNodeGuid = toNodeGuid;
|
||||
this.ConnectionType = connectionType;
|
||||
this.ConnectionInvokeType = connectionInvokeType;
|
||||
this.ChangeType = changeType;
|
||||
this.JunctionOfConnectionType = junctionOfConnectionType;
|
||||
}
|
||||
|
||||
public NodeConnectChangeEventArgs(string fromNodeGuid,
|
||||
string toNodeGuid,
|
||||
JunctionOfConnectionType junctionOfConnectionType, // 指示需要创建什么类型的连接线
|
||||
int argIndex,
|
||||
ConnectionArgSourceType connectionArgSourceType, // 节点对应的方法入参所需参数来源
|
||||
ConnectChangeType changeType) // 需要创建连接线还是删除连接线
|
||||
{
|
||||
this.FromNodeGuid = fromNodeGuid;
|
||||
this.ToNodeGuid = toNodeGuid;
|
||||
this.ChangeType = changeType;
|
||||
this.ArgIndex = argIndex;
|
||||
this.ConnectionArgSourceType = connectionArgSourceType;
|
||||
this.JunctionOfConnectionType = junctionOfConnectionType;
|
||||
|
||||
}
|
||||
/// <summary>
|
||||
/// 连接关系中始节点的Guid
|
||||
@@ -193,11 +215,22 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 连接类型
|
||||
/// </summary>
|
||||
public ConnectionType ConnectionType { get; protected set; }
|
||||
public ConnectionInvokeType ConnectionInvokeType { get; protected set; }
|
||||
/// <summary>
|
||||
/// 表示此次需要在两个节点之间创建连接关系,或是移除连接关系
|
||||
/// </summary>
|
||||
public ConnectChangeType ChangeType { get; protected set; }
|
||||
/// <summary>
|
||||
/// 指示需要创建什么类型的连接线
|
||||
/// </summary>
|
||||
public JunctionOfConnectionType JunctionOfConnectionType { get; protected set; }
|
||||
/// <summary>
|
||||
/// 节点对应的方法入参所需参数来源
|
||||
/// </summary>
|
||||
public ConnectionArgSourceType ConnectionArgSourceType { get; protected set; }
|
||||
public int ArgIndex { get; protected set; }
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -639,6 +672,13 @@ namespace Serein.Library.Api
|
||||
/// <returns></returns>
|
||||
Task StartAsyncInSelectNode(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 立刻调用某个节点,并获取其返回值
|
||||
/// </summary>
|
||||
/// <param name="nodeGuid">节点Guid</param>
|
||||
/// <returns></returns>
|
||||
Task<object> InvokeNodeAsync(string nodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行
|
||||
/// </summary>
|
||||
@@ -663,8 +703,16 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
/// <param name="fromNodeGuid">起始节点Guid</param>
|
||||
/// <param name="toNodeGuid">目标节点Guid</param>
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
Task<bool> ConnectNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionType connectionType);
|
||||
/// <param name="fromNodeJunctionType">起始节点控制点</param>
|
||||
/// <param name="toNodeJunctionType">目标节点控制点</param>
|
||||
/// <param name="connectionType">决定了方法执行后的后继行为</param>
|
||||
/// <param name="argIndex">决定了方法入参来源</param>
|
||||
Task<bool> ConnectNodeAsync(string fromNodeGuid,
|
||||
string toNodeGuid,
|
||||
JunctionType fromNodeJunctionType,
|
||||
JunctionType toNodeJunctionType,
|
||||
ConnectionInvokeType connectionType,
|
||||
int argIndex);
|
||||
|
||||
/// <summary>
|
||||
/// 创建节点/区域/基础控件
|
||||
@@ -680,7 +728,7 @@ namespace Serein.Library.Api
|
||||
/// <param name="fromNodeGuid">起始节点</param>
|
||||
/// <param name="toNodeGuid">目标节点</param>
|
||||
/// <param name="connectionType">连接类型</param>
|
||||
Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionType connectionType);
|
||||
Task<bool> RemoveConnectAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType);
|
||||
|
||||
/// <summary>
|
||||
/// 移除节点/区域/基础控件
|
||||
|
||||
28
Library/Enums/ConnectionArgSourceType.cs
Normal file
28
Library/Enums/ConnectionArgSourceType.cs
Normal file
@@ -0,0 +1,28 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
|
||||
/// <summary>
|
||||
/// 节点对应方法的入参来源
|
||||
/// </summary>
|
||||
public enum ConnectionArgSourceType
|
||||
{
|
||||
/// <summary>
|
||||
/// (连接自身)从上一节点获取数据
|
||||
/// </summary>
|
||||
GetPreviousNodeData,
|
||||
/// <summary>
|
||||
/// 从指定节点获取数据
|
||||
/// </summary>
|
||||
GetOtherNodeData,
|
||||
/// <summary>
|
||||
/// 立刻执行某个节点获取其数据
|
||||
/// </summary>
|
||||
GetOtherNodeDataOfInvoke,
|
||||
}
|
||||
}
|
||||
@@ -8,7 +8,7 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 表示了两个节点之间的连接关系,同时表示节点运行完成后,所会执行的下一个节点类型。
|
||||
/// </summary>
|
||||
public enum ConnectionType
|
||||
public enum ConnectionInvokeType
|
||||
{
|
||||
/// <summary>
|
||||
/// 将不会继续执行
|
||||
@@ -30,11 +30,8 @@ namespace Serein.Library
|
||||
/// 异常发生分支(当前节点对应的方法执行时出现非预期的异常)
|
||||
/// </summary>
|
||||
IsError,
|
||||
/// <summary>
|
||||
/// 无视
|
||||
/// </summary>
|
||||
// IsIgnore,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
27
Library/Enums/JunctionOfConnectionType.cs
Normal file
27
Library/Enums/JunctionOfConnectionType.cs
Normal file
@@ -0,0 +1,27 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接的控制点类型枚举
|
||||
/// </summary>
|
||||
public enum JunctionOfConnectionType
|
||||
{
|
||||
/// <summary>
|
||||
/// 没有关系,用于处理非预期连接的情况需要的返回值
|
||||
/// </summary>
|
||||
None,
|
||||
/// <summary>
|
||||
/// 表示方法执行顺序关系
|
||||
/// </summary>
|
||||
Invoke,
|
||||
/// <summary>
|
||||
/// 表示参数获取来源关系
|
||||
/// </summary>
|
||||
Arg
|
||||
}
|
||||
}
|
||||
@@ -7,7 +7,7 @@ using System.Threading.Tasks;
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 连接点类型
|
||||
/// 控制点类型
|
||||
/// </summary>
|
||||
public enum JunctionType
|
||||
{
|
||||
@@ -28,4 +28,8 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
NextStep,
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
56
Library/FlowNode/JunctionModel.cs
Normal file
56
Library/FlowNode/JunctionModel.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.FlowNode
|
||||
{
|
||||
|
||||
|
||||
/*
|
||||
* 有1个Execute
|
||||
* 有1个NextStep
|
||||
* 有0~65535个入参 ushort
|
||||
* 有1个ReturnData(void方法返回null)
|
||||
*
|
||||
* Execute: // 执行这个方法
|
||||
* 只接受 NextStep 的连接
|
||||
* ArgData:
|
||||
* 互相之间不能连接,只能接受 Execute、ReturnData 的连接
|
||||
* Execute:表示从 Execute所在节点 获取数据
|
||||
* ReturnData: 表示从对应节点获取数据
|
||||
* ReturnData:
|
||||
* 只能发起主动连接,且只能连接到 ArgData
|
||||
* NextStep
|
||||
* 只能连接连接 Execute
|
||||
*
|
||||
*/
|
||||
|
||||
/// <summary>
|
||||
/// 依附于节点的连接点
|
||||
/// </summary>
|
||||
public class JunctionModel
|
||||
{
|
||||
public JunctionModel(NodeModelBase NodeModel, JunctionType JunctionType)
|
||||
{
|
||||
Guid = System.Guid.NewGuid().ToString();
|
||||
this.NodeModel = NodeModel;
|
||||
this.JunctionType = JunctionType;
|
||||
}
|
||||
/// <summary>
|
||||
/// 用于标识连接点
|
||||
/// </summary>
|
||||
public string Guid { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 标识连接点的类型
|
||||
/// </summary>
|
||||
public JunctionType JunctionType { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 连接点依附的节点
|
||||
/// </summary>
|
||||
public NodeModelBase NodeModel { get; }
|
||||
}
|
||||
}
|
||||
@@ -3,6 +3,7 @@ using Serein.Library.NodeGenerator;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel;
|
||||
using System.Net.Mime;
|
||||
using System.Threading;
|
||||
|
||||
namespace Serein.Library
|
||||
@@ -71,8 +72,8 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private ConnectionType _nextOrientation = ConnectionType.None;
|
||||
//[PropertyInfo]
|
||||
//private ConnectionInvokeType _nextOrientation = ConnectionInvokeType.None;
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||
@@ -91,9 +92,9 @@ namespace Serein.Library
|
||||
{
|
||||
public NodeModelBase(IFlowEnvironment environment)
|
||||
{
|
||||
PreviousNodes = new Dictionary<ConnectionType, List<NodeModelBase>>();
|
||||
SuccessorNodes = new Dictionary<ConnectionType, List<NodeModelBase>>();
|
||||
foreach (ConnectionType ctType in NodeStaticConfig.ConnectionTypes)
|
||||
PreviousNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
||||
SuccessorNodes = new Dictionary<ConnectionInvokeType, List<NodeModelBase>>();
|
||||
foreach (ConnectionInvokeType ctType in NodeStaticConfig.ConnectionTypes)
|
||||
{
|
||||
PreviousNodes[ctType] = new List<NodeModelBase>();
|
||||
SuccessorNodes[ctType] = new List<NodeModelBase>();
|
||||
@@ -102,15 +103,17 @@ namespace Serein.Library
|
||||
this.Env = environment;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 不同分支的父节点
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionType, List<NodeModelBase>> PreviousNodes { get; }
|
||||
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> PreviousNodes { get; }
|
||||
|
||||
/// <summary>
|
||||
/// 不同分支的子节点
|
||||
/// </summary>
|
||||
public Dictionary<ConnectionType, List<NodeModelBase>> SuccessorNodes { get; }
|
||||
public Dictionary<ConnectionInvokeType, List<NodeModelBase>> SuccessorNodes { get; }
|
||||
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -6,6 +6,7 @@ using Serein.Library.Utils.SereinExpression;
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.ComponentModel.Design;
|
||||
using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http.Headers;
|
||||
@@ -24,22 +25,6 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
||||
{
|
||||
|
||||
|
||||
#region 调试中断
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 不再中断
|
||||
/// </summary>
|
||||
public void CancelInterrupt()
|
||||
{
|
||||
this.DebugSetting.InterruptClass = InterruptClass.None;
|
||||
DebugSetting.CancelInterruptCallback?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 导出/导入项目文件节点信息
|
||||
|
||||
/// <summary>
|
||||
@@ -56,10 +41,10 @@ namespace Serein.Library
|
||||
{
|
||||
// if (MethodDetails == null) return null;
|
||||
|
||||
var trueNodes = SuccessorNodes[ConnectionType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||
var falseNodes = SuccessorNodes[ConnectionType.IsFail].Select(item => item.Guid);// 假分支
|
||||
var errorNodes = SuccessorNodes[ConnectionType.IsError].Select(item => item.Guid);// 异常分支
|
||||
var upstreamNodes = SuccessorNodes[ConnectionType.Upstream].Select(item => item.Guid);// 上游分支
|
||||
var trueNodes = SuccessorNodes[ConnectionInvokeType.IsSucceed].Select(item => item.Guid); // 真分支
|
||||
var falseNodes = SuccessorNodes[ConnectionInvokeType.IsFail].Select(item => item.Guid);// 假分支
|
||||
var errorNodes = SuccessorNodes[ConnectionInvokeType.IsError].Select(item => item.Guid);// 异常分支
|
||||
var upstreamNodes = SuccessorNodes[ConnectionInvokeType.Upstream].Select(item => item.Guid);// 上游分支
|
||||
|
||||
// 生成参数列表
|
||||
Parameterdata[] parameterData = GetParameterdatas();
|
||||
@@ -86,7 +71,7 @@ namespace Serein.Library
|
||||
/// <returns></returns>
|
||||
public virtual NodeModelBase LoadInfo(NodeInfo nodeInfo)
|
||||
{
|
||||
this.Guid = nodeInfo.Guid;
|
||||
this.Guid = nodeInfo.Guid;
|
||||
|
||||
if (nodeInfo.Position is null)
|
||||
{
|
||||
@@ -104,6 +89,19 @@ namespace Serein.Library
|
||||
}
|
||||
return this;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 调试中断
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 不再中断
|
||||
/// </summary>
|
||||
public void CancelInterrupt()
|
||||
{
|
||||
this.DebugSetting.InterruptClass = InterruptClass.None;
|
||||
DebugSetting.CancelInterruptCallback?.Invoke();
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
@@ -137,8 +135,6 @@ namespace Serein.Library
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
@@ -163,7 +159,7 @@ namespace Serein.Library
|
||||
var currentNode = stack.Pop();
|
||||
|
||||
// 筛选出上游分支
|
||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionType.Upstream].ToArray();
|
||||
var upstreamNodes = currentNode.SuccessorNodes[ConnectionInvokeType.Upstream].ToArray();
|
||||
for (int index = 0; index < upstreamNodes.Length; index++)
|
||||
{
|
||||
NodeModelBase upstreamNode = upstreamNodes[index];
|
||||
@@ -176,7 +172,7 @@ namespace Serein.Library
|
||||
}
|
||||
upstreamNode.PreviousNode = currentNode;
|
||||
await upstreamNode.StartFlowAsync(context); // 执行流程节点的上游分支
|
||||
if (upstreamNode.NextOrientation == ConnectionType.IsError)
|
||||
if (context.NextOrientation == ConnectionInvokeType.IsError)
|
||||
{
|
||||
// 如果上游分支执行失败,不再继续执行
|
||||
// 使上游节点(仅上游节点本身,不包含上游节点的后继节点)
|
||||
@@ -197,7 +193,7 @@ namespace Serein.Library
|
||||
#region 执行完成
|
||||
|
||||
// 选择后继分支
|
||||
var nextNodes = currentNode.SuccessorNodes[currentNode.NextOrientation];
|
||||
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
|
||||
|
||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
||||
@@ -215,7 +211,6 @@ namespace Serein.Library
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 执行节点对应的方法
|
||||
/// </summary>
|
||||
@@ -234,7 +229,6 @@ namespace Serein.Library
|
||||
#endregion
|
||||
|
||||
MethodDetails md = MethodDetails;
|
||||
//var del = md.MethodDelegate.Clone();
|
||||
if (md is null)
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
@@ -247,35 +241,70 @@ namespace Serein.Library
|
||||
{
|
||||
md.ActingInstance = context.Env.IOC.Get(md.ActingInstanceType);
|
||||
}
|
||||
// md.ActingInstance ??= context.Env.IOC.Get(md.ActingInstanceType);
|
||||
object instance = md.ActingInstance;
|
||||
|
||||
|
||||
object result = null;
|
||||
|
||||
try
|
||||
{
|
||||
object[] args = GetParameters(context, this, md);
|
||||
result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||
NextOrientation = ConnectionType.IsSucceed;
|
||||
object[] args = await GetParametersAsync(context, this, md);
|
||||
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||
NextOrientation = ConnectionType.IsError;
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
RuningException = ex;
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 执行单个节点对应的方法,并不做状态检查
|
||||
/// </summary>
|
||||
/// <param name="env"></param>
|
||||
/// <returns></returns>
|
||||
public virtual async Task<object> InvokeAsync(IFlowEnvironment env)
|
||||
{
|
||||
try
|
||||
{
|
||||
MethodDetails md = MethodDetails;
|
||||
if (md is null)
|
||||
{
|
||||
throw new Exception($"不存在方法信息{md.MethodName}");
|
||||
}
|
||||
if (!env.TryGetDelegateDetails(md.MethodName, out var dd))
|
||||
{
|
||||
throw new Exception($"不存在对应委托{md.MethodName}");
|
||||
}
|
||||
if (md.ActingInstance is null)
|
||||
{
|
||||
md.ActingInstance = env.IOC.Get(md.ActingInstanceType);
|
||||
if (md.ActingInstance is null)
|
||||
{
|
||||
md.ActingInstance = env.IOC.Instantiate(md.ActingInstanceType);
|
||||
if (md.ActingInstance is null)
|
||||
{
|
||||
throw new Exception($"无法创建相应的实例{md.ActingInstanceType.FullName}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object[] args = await GetParametersAsync(null, this, md);
|
||||
var result = await dd.InvokeAsync(md.ActingInstance, args);
|
||||
return result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取对应的参数数组
|
||||
/// </summary>
|
||||
public static object[] GetParameters(IDynamicContext context, NodeModelBase nodeModel, MethodDetails md)
|
||||
public static async Task<object[]> GetParametersAsync(IDynamicContext context, NodeModelBase nodeModel, MethodDetails md)
|
||||
{
|
||||
await Task.Delay(0);
|
||||
// 用正确的大小初始化参数数组
|
||||
if (md.ParameterDetailss.Length == 0)
|
||||
{
|
||||
@@ -283,22 +312,34 @@ namespace Serein.Library
|
||||
}
|
||||
|
||||
object[] parameters = new object[md.ParameterDetailss.Length];
|
||||
var flowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
|
||||
var previousDataType = flowData?.GetType();
|
||||
var previousFlowData = nodeModel.PreviousNode?.FlowData; // 当前传递的数据
|
||||
var previousDataType = previousFlowData?.GetType(); // 当前传递数据的类型
|
||||
|
||||
for (int i = 0; i < parameters.Length; i++)
|
||||
{
|
||||
|
||||
object inputParameter; // 存放解析的临时参数
|
||||
var ed = md.ParameterDetailss[i]; // 方法入参描述
|
||||
|
||||
#region 获取基础的上下文数据
|
||||
if (ed.DataType == typeof(IFlowEnvironment)) // 获取流程上下文
|
||||
{
|
||||
parameters[i] = nodeModel.Env;
|
||||
continue;
|
||||
}
|
||||
if (ed.DataType == typeof(IDynamicContext)) // 获取流程上下文
|
||||
{
|
||||
parameters[i] = context;
|
||||
continue;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 确定[预入参]数据
|
||||
object inputParameter; // 存放解析的临时参数
|
||||
if (ed.IsExplicitData) // 判断是否使用显示的输入参数
|
||||
{
|
||||
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && !(flowData is null))
|
||||
if (ed.DataValue.StartsWith("@get", StringComparison.OrdinalIgnoreCase) && !(previousFlowData is null))
|
||||
{
|
||||
// 执行表达式从上一节点获取对象
|
||||
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, flowData, out _);
|
||||
inputParameter = SerinExpressionEvaluator.Evaluate(ed.DataValue, previousFlowData, out _);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -308,9 +349,31 @@ namespace Serein.Library
|
||||
}
|
||||
else
|
||||
{
|
||||
inputParameter = flowData; // 使用上一节点的对象
|
||||
}
|
||||
if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||
{
|
||||
inputParameter = previousFlowData; // 使用运行时上一节点的返回值
|
||||
}
|
||||
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetPreviousNodeData)
|
||||
{
|
||||
// 获取指定节点的数据
|
||||
// 如果指定节点没有被执行,会返回null
|
||||
// 如果执行过,会获取上一次执行结果作为预入参数据
|
||||
inputParameter = ed.ArgDataSourceNodeMoels[i].FlowData;
|
||||
}
|
||||
else if (ed.ArgDataSourceType == ConnectionArgSourceType.GetOtherNodeDataOfInvoke)
|
||||
{
|
||||
// 立刻调用对应节点获取数据。
|
||||
var result = await ed.ArgDataSourceNodeMoels[i].InvokeAsync(nodeModel.Env);
|
||||
inputParameter = result;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new Exception("节点执行方法获取入参参数时,ConnectionArgSourceType枚举是意外的枚举值");
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 入参存在取值转换器,调用对应的转换器获取入参数据
|
||||
// 入参存在取值转换器
|
||||
if (ed.ExplicitType.IsEnum && !(ed.Convertor is null))
|
||||
{
|
||||
@@ -327,13 +390,11 @@ namespace Serein.Library
|
||||
parameters[i] = value;
|
||||
continue;
|
||||
}
|
||||
//if (Enum.TryParse(ed.ExplicitType, ed.DataValue, out var resultEnum))
|
||||
//{
|
||||
|
||||
//}
|
||||
}
|
||||
#endregion
|
||||
|
||||
// 入参存在类型转换器,获取枚举转换器中记录的枚举
|
||||
#region 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
|
||||
// 入参存在基于BinValue的类型转换器,获取枚举转换器中记录的类型
|
||||
if (ed.ExplicitType.IsEnum && ed.DataType != ed.ExplicitType)
|
||||
{
|
||||
var resultEnum = Enum.Parse(ed.ExplicitType, ed.DataValue);
|
||||
@@ -341,7 +402,7 @@ namespace Serein.Library
|
||||
var type = EnumHelper.GetBoundValue(ed.ExplicitType, resultEnum, attr => attr.Value);
|
||||
if (type is Type enumBindType && !(enumBindType is null))
|
||||
{
|
||||
var value = context.Env.IOC.Instantiate(enumBindType);
|
||||
var value = nodeModel.Env.IOC.Instantiate(enumBindType);
|
||||
if (value is null)
|
||||
{
|
||||
|
||||
@@ -351,62 +412,83 @@ namespace Serein.Library
|
||||
parameters[i] = value;
|
||||
continue;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
#region 对入参数据尝试进行转换
|
||||
|
||||
if (ed.DataType.IsValueType)
|
||||
if (inputParameter.GetType() == ed.DataType)
|
||||
{
|
||||
var valueStr = inputParameter?.ToString();
|
||||
parameters[i] = valueStr.ToValueData(ed.DataType);
|
||||
parameters[i] = inputParameter; // 类型一致无需转换,直接装入入参数组
|
||||
}
|
||||
else
|
||||
else if (ed.DataType.IsValueType)
|
||||
{
|
||||
// 值类型
|
||||
var valueStr = inputParameter?.ToString();
|
||||
if (ed.DataType == typeof(string))
|
||||
parameters[i] = valueStr.ToValueData(ed.DataType); // 类型不一致,尝试进行转换,如果转换失败返回类型对应的默认值
|
||||
}
|
||||
else
|
||||
{
|
||||
// 引用类型
|
||||
if (ed.DataType == typeof(string)) // 转为字符串
|
||||
{
|
||||
var valueStr = inputParameter?.ToString();
|
||||
parameters[i] = valueStr;
|
||||
}
|
||||
else if (ed.DataType == typeof(IDynamicContext))
|
||||
else if(ed.DataType.IsSubclassOf(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 子类/实现类
|
||||
{
|
||||
parameters[i] = context;
|
||||
// 方法入参中,父类不能隐式转为子类,这里需要进行强制转换
|
||||
parameters[i] = ObjectConvertHelper.ConvertParentToChild(inputParameter, ed.DataType);
|
||||
}
|
||||
else if (ed.DataType == typeof(MethodDetails))
|
||||
{
|
||||
parameters[i] = md;
|
||||
}
|
||||
else if (ed.DataType == typeof(NodeModelBase))
|
||||
{
|
||||
parameters[i] = nodeModel;
|
||||
}
|
||||
else
|
||||
else if(ed.DataType.IsAssignableFrom(inputParameter.GetType())) // 入参类型 是 预入参数据类型 的 父类/接口
|
||||
{
|
||||
parameters[i] = inputParameter;
|
||||
}
|
||||
// 集合类型
|
||||
else if(inputParameter is IEnumerable collection)
|
||||
{
|
||||
var enumerableMethods = typeof(Enumerable).GetMethods(); // 获取所有的 Enumerable 扩展方法
|
||||
MethodInfo conversionMethod;
|
||||
if (ed.DataType.IsArray) // 转为数组
|
||||
{
|
||||
parameters[i] = inputParameter;
|
||||
conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToArray" && m.IsGenericMethodDefinition);
|
||||
}
|
||||
else if (ed.DataType.GetGenericTypeDefinition() == typeof(List<>)) // 转为集合
|
||||
{
|
||||
conversionMethod = enumerableMethods.FirstOrDefault(m => m.Name == "ToList" && m.IsGenericMethodDefinition);
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new InvalidOperationException("输入对象不是集合或目标类型不支持(目前仅支持Array、List的自动转换)");
|
||||
}
|
||||
var genericMethod = conversionMethod.MakeGenericMethod(ed.DataType);
|
||||
var result = genericMethod.Invoke(null, new object[] { collection });
|
||||
parameters[i] = result;
|
||||
}
|
||||
|
||||
|
||||
//parameters[i] = ed.DataType switch
|
||||
|
||||
//else if (ed.DataType == typeof(MethodDetails)) // 希望获取节点对应的方法描述,好像没啥用
|
||||
//{
|
||||
// Type t when t == typeof(string) => valueStr,
|
||||
// Type t when t == typeof(IDynamicContext) => context, // 上下文
|
||||
// Type t when t == typeof(DateTime) => string.IsNullOrEmpty(valueStr) ? null : DateTime.Parse(valueStr),
|
||||
|
||||
// Type t when t == typeof(MethodDetails) => md, // 节点方法描述
|
||||
// Type t when t == typeof(NodeModelBase) => nodeModel, // 节点实体类
|
||||
|
||||
// Type t when t.IsArray => (inputParameter as Array)?.Cast<object>().ToList(),
|
||||
// Type t when t.IsGenericType && t.GetGenericTypeDefinition() == typeof(List<>) => inputParameter,
|
||||
// _ => inputParameter,
|
||||
//};
|
||||
}
|
||||
// parameters[i] = md;
|
||||
//}
|
||||
//else if (ed.DataType == typeof(NodeModelBase)) // 希望获取方法生成的节点,好像没啥用
|
||||
//{
|
||||
// parameters[i] = nodeModel;
|
||||
//}
|
||||
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
||||
}
|
||||
return parameters;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 更新节点数据,并检查监视表达式是否生效
|
||||
/// </summary>
|
||||
@@ -479,7 +561,6 @@ namespace Serein.Library
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 释放对象
|
||||
/// </summary>
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
using Serein.Library.Api;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics.Contracts;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
@@ -17,7 +14,7 @@ namespace Serein.Library
|
||||
private readonly IFlowEnvironment env;
|
||||
|
||||
/// <summary>
|
||||
/// 对应的节点
|
||||
/// 所在的节点
|
||||
/// </summary>
|
||||
[PropertyInfo(IsProtection = true)]
|
||||
private NodeModelBase _nodeModel;
|
||||
@@ -29,7 +26,9 @@ namespace Serein.Library
|
||||
private int _index;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为显式参数(固定值/表达式)
|
||||
/// <para>是否为显式参数(固定值/表达式)</para>
|
||||
/// <para>如果为 true ,则使用UI输入的文本值作为入参数据(过程中会尽可能转为类型需要的数据)。</para>
|
||||
/// <para>如果为 false ,则根据 ArgDataSourceType 调用相应节点的GetFlowData()方法,获取返回的数据作为入参数据。</para>
|
||||
/// </summary>
|
||||
[PropertyInfo(IsNotification = true)]
|
||||
private bool _isExplicitData ;
|
||||
@@ -41,7 +40,7 @@ namespace Serein.Library
|
||||
private Func<object, object> _convertor ;
|
||||
|
||||
/// <summary>
|
||||
/// 显式类型
|
||||
/// 方法入参若无相关转换器特性标注,则无需关注该变量。该变量用于需要用到枚举BinValue转换器时,指示相应的入参变量需要转为的类型。
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private Type _explicitType ;
|
||||
@@ -56,7 +55,22 @@ namespace Serein.Library
|
||||
private string _explicitTypeName ;
|
||||
|
||||
/// <summary>
|
||||
/// 方法需要的类型
|
||||
/// 入参数据来源。默认使用上一节点作为入参数据。
|
||||
/// </summary>
|
||||
[PropertyInfo(IsNotification = true)]
|
||||
private ConnectionArgSourceType _argDataSourceType = ConnectionArgSourceType.GetPreviousNodeData;
|
||||
|
||||
/// <summary>
|
||||
/// 当 ArgDataSourceType 不为 GetPreviousNodeData 时(从运行时上一节点获取数据)。
|
||||
/// 则通过该集合对应的节点,获取其 FlowData 作为预处理的入参参数。
|
||||
/// </summary>
|
||||
[PropertyInfo(IsProtection = true)]
|
||||
public NodeModelBase[] _argDataSourceNodeMoels;
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 方法入参需要的类型。
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private Type _dataType ;
|
||||
@@ -74,7 +88,7 @@ namespace Serein.Library
|
||||
private string _dataValue;
|
||||
|
||||
/// <summary>
|
||||
/// 如果是引用类型,拷贝时不会发生改变。
|
||||
/// 只有当ExplicitTypeName 为 Select 时,才会需要该成员。
|
||||
/// </summary>
|
||||
[PropertyInfo(IsNotification = true)]
|
||||
private string[] _items ;
|
||||
@@ -91,6 +105,7 @@ namespace Serein.Library
|
||||
this.env = env;
|
||||
this.NodeModel = nodeModel;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 通过参数信息加载实体,用于加载项目文件、远程连接的场景
|
||||
/// </summary>
|
||||
@@ -109,12 +124,15 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 用于创建元数据
|
||||
/// </summary>
|
||||
/// <param name="info">方法参数信息</param>
|
||||
public ParameterDetails()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 转为描述
|
||||
/// </summary>
|
||||
@@ -151,6 +169,7 @@ namespace Serein.Library
|
||||
Name = this.Name,
|
||||
DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue,
|
||||
Items = this.Items?.Select(it => it).ToArray(),
|
||||
|
||||
};
|
||||
return pd;
|
||||
}
|
||||
|
||||
@@ -29,12 +29,12 @@ namespace Serein.Library
|
||||
/// <summary>
|
||||
/// 节点连接关系种类
|
||||
/// </summary>
|
||||
public static readonly ConnectionType[] ConnectionTypes = new ConnectionType[]
|
||||
public static readonly ConnectionInvokeType[] ConnectionTypes = new ConnectionInvokeType[]
|
||||
{
|
||||
ConnectionType.Upstream,
|
||||
ConnectionType.IsSucceed,
|
||||
ConnectionType.IsFail,
|
||||
ConnectionType.IsError,
|
||||
ConnectionInvokeType.Upstream,
|
||||
ConnectionInvokeType.IsSucceed,
|
||||
ConnectionInvokeType.IsFail,
|
||||
ConnectionInvokeType.IsError,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
82
Library/Utils/ObjectConvertHelper.cs
Normal file
82
Library/Utils/ObjectConvertHelper.cs
Normal file
@@ -0,0 +1,82 @@
|
||||
using System;
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
public static class ObjectConvertHelper
|
||||
{
|
||||
/// <summary>
|
||||
/// 父类转为子类
|
||||
/// </summary>
|
||||
/// <param name="parent">父类对象</param>
|
||||
/// <param name="childType">子类类型</param>
|
||||
/// <returns></returns>
|
||||
public static object ConvertParentToChild(object parent,Type childType)
|
||||
{
|
||||
var child = Activator.CreateInstance(childType);
|
||||
var parentType = parent.GetType();
|
||||
|
||||
// 复制父类属性
|
||||
foreach (var prop in parentType.GetProperties())
|
||||
{
|
||||
if (prop.CanWrite)
|
||||
{
|
||||
var value = prop.GetValue(parent);
|
||||
childType.GetProperty(prop.Name)?.SetValue(child, value);
|
||||
}
|
||||
}
|
||||
return child;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 集合类型转换为Array/List
|
||||
/// </summary>
|
||||
/// <param name="obj"></param>
|
||||
/// <param name="targetType"></param>
|
||||
/// <returns></returns>
|
||||
/// <exception cref="InvalidOperationException"></exception>
|
||||
public static object ConvertToEnumerableType(object obj, Type targetType)
|
||||
{
|
||||
// 获取目标类型的元素类型
|
||||
Type targetElementType = targetType.IsArray
|
||||
? targetType.GetElementType()
|
||||
: targetType.GetGenericArguments().FirstOrDefault();
|
||||
|
||||
if (targetElementType == null)
|
||||
throw new InvalidOperationException("无法获取目标类型的元素类型");
|
||||
|
||||
// 检查输入对象是否为集合类型
|
||||
if (obj is IEnumerable collection)
|
||||
{
|
||||
// 判断目标类型是否是数组
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
var toArrayMethod = typeof(Enumerable).GetMethod("ToArray").MakeGenericMethod(targetElementType);
|
||||
return toArrayMethod.Invoke(null, new object[] { collection });
|
||||
}
|
||||
// 判断目标类型是否是 List<T>
|
||||
else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(List<>))
|
||||
{
|
||||
var toListMethod = typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(targetElementType);
|
||||
return toListMethod.Invoke(null, new object[] { collection });
|
||||
}
|
||||
// 判断目标类型是否是 HashSet<T>
|
||||
else if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(HashSet<>))
|
||||
{
|
||||
var toHashSetMethod = typeof(Enumerable).GetMethod("ToHashSet").MakeGenericMethod(targetElementType);
|
||||
return toHashSetMethod.Invoke(null, new object[] { collection });
|
||||
}
|
||||
// 其他类型可以扩展类似的处理
|
||||
}
|
||||
|
||||
throw new InvalidOperationException("输入对象不是集合或目标类型不支持");
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -88,10 +88,8 @@ namespace Serein.Library.Utils
|
||||
{
|
||||
var constructor = type.GetConstructors().First(); // 获取第一个构造函数
|
||||
var parameters = constructor.GetParameters(); // 获取参数列表
|
||||
var parameterValues = parameters.Select(param => ResolveDependency(param.ParameterType)).ToArray();
|
||||
var instance = Activator.CreateInstance(type, parameterValues);
|
||||
|
||||
//var instance =CreateInstance(controllerType, parameters); // CreateInstance(controllerType, parameters); // 创建目标类型的实例
|
||||
var parameterValues = parameters.Select(param => ResolveDependency(param.ParameterType)).ToArray(); // 生成创建类型的入参参数
|
||||
var instance = Activator.CreateInstance(type, parameterValues); // 创建实例
|
||||
if (instance != null)
|
||||
{
|
||||
InjectDependencies(instance, false); // 完成创建后注入实例需要的特性依赖项
|
||||
|
||||
Reference in New Issue
Block a user