mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-02 15:50:47 +08:00
完善节点图的代码生成
This commit is contained in:
@@ -87,7 +87,15 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
/// <param name="nodeModel"></param>
|
||||
/// <param name="flowData"></param>
|
||||
void AddOrUpdate(string nodeModel, FlowResult flowData);
|
||||
void AddOrUpdateFlowData(string nodeModel, FlowResult flowData);
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新当前节点的数据
|
||||
/// </summary>
|
||||
/// <param name="nodeModel"></param>
|
||||
/// <param name="data"></param>
|
||||
void AddOrUpdate(string nodeModel, object data);
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 重置流程状态(用于对象池回收)
|
||||
|
||||
@@ -29,7 +29,7 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
/// <param name="startNodeGuid"></param>
|
||||
/// <returns></returns>
|
||||
Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid);
|
||||
Task<TResult> StartFlowAsync<TResult>(string startNodeGuid);
|
||||
|
||||
/// <summary>
|
||||
/// 结束运行
|
||||
|
||||
@@ -236,7 +236,7 @@ namespace Serein.Library
|
||||
#endregion
|
||||
|
||||
#region 执行完成时更新栈
|
||||
context.AddOrUpdate(currentNode.Guid, flowResult); // 上下文中更新数据
|
||||
context.AddOrUpdateFlowData(currentNode.Guid, flowResult); // 上下文中更新数据
|
||||
|
||||
// 首先将指定类别后继分支的所有节点逆序推入栈中
|
||||
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
|
||||
|
||||
@@ -217,12 +217,23 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
/// <param name="nodeModel">节点</param>
|
||||
/// <param name="flowData">新的数据</param>
|
||||
public void AddOrUpdate(string nodeModel, FlowResult flowData)
|
||||
public void AddOrUpdateFlowData(string nodeModel, FlowResult flowData)
|
||||
{
|
||||
// this.dictNodeFlowData.TryGetValue(nodeGuid, out var oldFlowData);
|
||||
dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o,n ) => flowData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 添加或更新当前节点的数据
|
||||
/// </summary>
|
||||
/// <param name="nodeModel"></param>
|
||||
/// <param name="data"></param>
|
||||
public void AddOrUpdate(string nodeModel, object data)
|
||||
{
|
||||
var flowData = new FlowResult(nodeModel, this, data);
|
||||
dictNodeFlowData.AddOrUpdate(nodeModel, _ => flowData, (o, n) => flowData);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 上一节点数据透传到下一节点
|
||||
/// </summary>
|
||||
|
||||
@@ -1,101 +1,89 @@
|
||||
using Microsoft.VisualBasic;
|
||||
using Microsoft.CodeAnalysis.CSharp.Syntax;
|
||||
using Microsoft.Extensions.ObjectPool;
|
||||
using Microsoft.VisualBasic;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.FlowNode
|
||||
namespace Serein.Library
|
||||
{
|
||||
|
||||
|
||||
/*public class FlowControl
|
||||
/*
|
||||
public class CallNodeLookup : IFlowCallTree
|
||||
{
|
||||
private DynamicFlowTemplate dynamicFlowTemplate;
|
||||
private LightweightEnvironment environment;
|
||||
public FlowControl(DynamicFlowTemplate dynamicFlowTemplate,
|
||||
LightweightEnvironment environment)
|
||||
private static readonly string[] _keys = new[]
|
||||
{
|
||||
var callTree = new CallTree()
|
||||
callTree.SetMainNode("node1_guid");
|
||||
"Start", // 0
|
||||
"Stop", // 1
|
||||
"Reset", // 2
|
||||
"Pause", // 3
|
||||
"Resume", // 4
|
||||
"Check", // 5
|
||||
"Init", // 6
|
||||
"Load", // 7
|
||||
"Save", // 8
|
||||
"Clear" // 9
|
||||
};
|
||||
|
||||
callTree.AddCallNode("node1_guid", (context) => DynamicFlowTemplate.Method(context, ...));
|
||||
callTree.AddCallNode("node2_guid", (context) => DynamicFlowTemplate.Method(context, ...));
|
||||
callTree.AddCallNode("node3_guid", (context) => DynamicFlowTemplate.Method(context, ...));
|
||||
callTree.AddCallNode("node4_guid", (context) => DynamicFlowTemplate.Method(context, ...));
|
||||
private static readonly CallNode[] _values = new CallNode[10];
|
||||
|
||||
callTree["node1_guid"].AddFlowUpstream("node2_guid");
|
||||
callTree["node1_guid"].AddFlowSucceed("node2_guid");
|
||||
callTree["node2_guid"].AddFlowSucceed("node3_guid");
|
||||
callTree["node2_guid"].AddFlowError("node4_guid");
|
||||
|
||||
LightweightEnvironment.LoadProject(callTree);
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
/*public class DynamicFlowTemplate
|
||||
{
|
||||
private readonly class1_type class1_instance;
|
||||
|
||||
public DynamicFlowTemplate(class1_type class_instance){
|
||||
this.class1_instance = class1_instance;
|
||||
static CallNodeLookup()
|
||||
{
|
||||
*//*_values[0] = new CallNode("Start");
|
||||
_values[1] = new CallNode("Stop");
|
||||
_values[2] = new CallNode("Reset");
|
||||
_values[3] = new CallNode("Pause");
|
||||
_values[4] = new CallNode("Resume");
|
||||
_values[5] = new CallNode("Check");
|
||||
_values[6] = new CallNode("Init");
|
||||
_values[7] = new CallNode("Load");
|
||||
_values[8] = new CallNode("Save");
|
||||
_values[9] = new CallNode("Clear");*//*
|
||||
}
|
||||
|
||||
[Description("node1_guid")]
|
||||
[Description("assembly_name.class_name.method_name")]
|
||||
private [methodReturnType / void] NodeMethod_[method_name](IDynamicContext context, type1 param1, type2 param2)
|
||||
// 最小冲突哈希函数(简单示例,固定键集有效)
|
||||
private static int PerfectHash(string key)
|
||||
{
|
||||
class1_instance.method(params);
|
||||
context.SetData("node_guid", null);
|
||||
return key switch
|
||||
{
|
||||
"Start" => 0,
|
||||
"Stop" => 1,
|
||||
"Reset" => 2,
|
||||
"Pause" => 3,
|
||||
"Resume" => 4,
|
||||
"Check" => 5,
|
||||
"Init" => 6,
|
||||
"Load" => 7,
|
||||
"Save" => 8,
|
||||
"Clear" => 9,
|
||||
_ => -1
|
||||
};
|
||||
}
|
||||
|
||||
[Description("node2_guid")]
|
||||
[Description("assembly_name.class_name.method_name")]
|
||||
private [methodReturnType] NodeMethod_[method_name](IDynamicContext context, type1 param1)
|
||||
public CallNode Get(string key)
|
||||
{
|
||||
param1 = context.GetData("node1_guid") as type1;
|
||||
var result = class1_instance.method(params, param1);
|
||||
context.SetData("node2_guid", result);
|
||||
}
|
||||
|
||||
|
||||
[Description("node3_guid")]
|
||||
public [methodReturnType] FlowCall_[method_name](IDynamicContext context, type1 param1, type2 param2)
|
||||
{
|
||||
param1 = context.GetData("node3_guid") as type1;
|
||||
var result = class1_instance.method(params, param1);
|
||||
context.SetData("node3_guid", result);
|
||||
int index = PerfectHash(key);
|
||||
if (index >= 0 && _keys[index] == key)
|
||||
return _values[index];
|
||||
return null;
|
||||
}
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class CallTree
|
||||
public class FlowCallTree : IFlowCallTree
|
||||
{
|
||||
public CallTree(string canvasGuid, string startNodeGuid)
|
||||
{
|
||||
_canvas_guid = canvasGuid;
|
||||
_start_node_guid = startNodeGuid;
|
||||
}
|
||||
|
||||
private readonly SortedDictionary<string, CallNode> _callNodes = new SortedDictionary<string,CallNode>();
|
||||
//private readonly Dictionary<string, CallNode> _callNodes = new Dictionary<string,CallNode>();
|
||||
|
||||
private readonly ConcurrentDictionary<string, CallNode> _callNodes = new ConcurrentDictionary<string,CallNode>();
|
||||
private readonly string _start_node_guid;
|
||||
private readonly string _canvas_guid ;
|
||||
public CallNode this[string index]
|
||||
{
|
||||
get
|
||||
@@ -106,45 +94,57 @@ namespace Serein.Library.FlowNode
|
||||
set
|
||||
{
|
||||
// 设置指定索引的值
|
||||
_callNodes.AddOrUpdate(index, value, (key, oldValue) => value);
|
||||
_callNodes.Add(index, value);
|
||||
}
|
||||
}
|
||||
|
||||
public void AddCallNode(string nodeGuid, Action<IDynamicContext> action)
|
||||
{
|
||||
var node = new CallNode(this, nodeGuid, action);
|
||||
var node = new CallNode(nodeGuid, action);
|
||||
_callNodes[nodeGuid] = node;
|
||||
}
|
||||
public void AddCallNode(string nodeGuid, Func<IDynamicContext, Task> func)
|
||||
{
|
||||
var node = new CallNode(this, nodeGuid, func);
|
||||
var node = new CallNode(nodeGuid, func);
|
||||
_callNodes[nodeGuid] = node;
|
||||
}
|
||||
|
||||
public CallNode Get(string key)
|
||||
{
|
||||
return _callNodes.TryGetValue(key, out CallNode callNode) ? callNode : null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
public class CallNode
|
||||
{
|
||||
|
||||
private readonly Func<IDynamicContext, Task> func;
|
||||
private readonly CallTree callTree;
|
||||
private readonly Action<IDynamicContext> action;
|
||||
private Func<IDynamicContext, Task> taskFunc;
|
||||
private Action<IDynamicContext> action;
|
||||
|
||||
public CallNode(CallTree callTree, string nodeGuid, Action<IDynamicContext> action)
|
||||
{
|
||||
this.callTree = callTree;
|
||||
Guid = nodeGuid;
|
||||
this.action = action;
|
||||
}
|
||||
|
||||
public CallNode(CallTree callTree, string nodeGuid, Func<IDynamicContext, Task> func)
|
||||
public CallNode(string nodeGuid)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.func = func;
|
||||
Init();
|
||||
}
|
||||
public CallNode(string nodeGuid, Action<IDynamicContext> action)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.action = action;
|
||||
Init();
|
||||
}
|
||||
|
||||
public CallNode(string nodeGuid, Func<IDynamicContext, Task> func)
|
||||
{
|
||||
Guid = nodeGuid;
|
||||
this.taskFunc = func;
|
||||
Init();
|
||||
}
|
||||
|
||||
|
||||
private void Init()
|
||||
{
|
||||
@@ -157,6 +157,16 @@ namespace Serein.Library.FlowNode
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void SetAction(Action<IDynamicContext> action)
|
||||
{
|
||||
this.action = action;
|
||||
}
|
||||
public void SetAction(Func<IDynamicContext, Task> taskFunc)
|
||||
{
|
||||
this.taskFunc = taskFunc;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 对应的节点
|
||||
@@ -173,28 +183,30 @@ namespace Serein.Library.FlowNode
|
||||
public Dictionary<ConnectionInvokeType, List<CallNode>> SuccessorNodes { get; private set; }
|
||||
|
||||
|
||||
public CallNode AddChildNodeUpstream(string nodeGuid)
|
||||
public CallNode AddChildNodeUpstream(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.Upstream;
|
||||
PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
return this;
|
||||
}
|
||||
public CallNode AddChildNodeSucceed(string nodeGuid)
|
||||
public CallNode AddChildNodeSucceed(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsSucceed;
|
||||
PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
|
||||
var connectionInvokeType = ConnectionInvokeType.IsSucceed;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
public CallNode AddChildNodeFail(string nodeGuid)
|
||||
public CallNode AddChildNodeFail(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsFail;
|
||||
PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
|
||||
var connectionInvokeType = ConnectionInvokeType.IsFail;
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
|
||||
return this;
|
||||
}
|
||||
public CallNode AddChildNodeError(string nodeGuid)
|
||||
public CallNode AddChildNodeError(CallNode callNode)
|
||||
{
|
||||
var connectionInvokeType = ConnectionInvokeType.IsError;
|
||||
PreviousNodes[connectionInvokeType].Add(callTree[nodeGuid]);
|
||||
SuccessorNodes[connectionInvokeType].Add(callNode);
|
||||
return this;
|
||||
}
|
||||
|
||||
@@ -216,9 +228,9 @@ namespace Serein.Library.FlowNode
|
||||
{
|
||||
action(context);
|
||||
}
|
||||
else if (func is not null)
|
||||
else if (taskFunc is not null)
|
||||
{
|
||||
await func(context);
|
||||
await taskFunc(context);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -226,6 +238,10 @@ namespace Serein.Library.FlowNode
|
||||
}
|
||||
}
|
||||
|
||||
private static readonly DefaultObjectPool<Stack<CallNode>> _stackPool = new DefaultObjectPool<Stack<CallNode>>(new DefaultPooledObjectPolicy<Stack<CallNode>>());
|
||||
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 开始执行
|
||||
/// </summary>
|
||||
@@ -234,10 +250,8 @@ namespace Serein.Library.FlowNode
|
||||
/// <returns></returns>
|
||||
public async Task<FlowResult> StartFlowAsync(IDynamicContext context, CancellationToken token)
|
||||
{
|
||||
Stack<CallNode> stack = new Stack<CallNode>();
|
||||
HashSet<CallNode> processedNodes = new HashSet<CallNode>(); // 用于记录已处理上游节点的节点
|
||||
var stack = _stackPool.Get();
|
||||
stack.Push(this);
|
||||
|
||||
while (true)
|
||||
{
|
||||
if (token.IsCancellationRequested)
|
||||
@@ -253,7 +267,6 @@ namespace Serein.Library.FlowNode
|
||||
try
|
||||
{
|
||||
await currentNode.InvokeAsync(context, token);
|
||||
|
||||
if (context.NextOrientation == ConnectionInvokeType.None) // 没有手动设置时,进行自动设置
|
||||
{
|
||||
context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||
@@ -269,7 +282,6 @@ namespace Serein.Library.FlowNode
|
||||
#endregion
|
||||
|
||||
#region 执行完成时更新栈
|
||||
context.AddOrUpdate(currentNode.Guid, flowResult); // 上下文中更新数据
|
||||
|
||||
// 首先将指定类别后继分支的所有节点逆序推入栈中
|
||||
var nextNodes = currentNode.SuccessorNodes[context.NextOrientation];
|
||||
@@ -293,35 +305,51 @@ namespace Serein.Library.FlowNode
|
||||
|
||||
if (stack.Count == 0)
|
||||
{
|
||||
_stackPool.Return(stack);
|
||||
flowResult = context.GetFlowData(currentNode.Guid);
|
||||
return flowResult; // 说明流程到了终点
|
||||
}
|
||||
|
||||
if (context.RunState == RunState.Completion)
|
||||
{
|
||||
|
||||
_stackPool.Return(stack);
|
||||
context.Env.WriteLine(InfoType.INFO, $"流程执行到节点[{currentNode.Guid}]时提前结束,将返回当前执行结果。");
|
||||
flowResult = context.GetFlowData(currentNode.Guid); _stackPool.Return(stack);
|
||||
return flowResult; // 流程执行完成,返回结果
|
||||
}
|
||||
|
||||
if (token.IsCancellationRequested)
|
||||
{
|
||||
_stackPool.Return(stack);
|
||||
throw new Exception($"流程执行到节点[{currentNode.Guid}]时被取消,未能获取到流程结果。");
|
||||
}
|
||||
|
||||
|
||||
#endregion
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public interface IFlowCallTree
|
||||
{
|
||||
CallNode Get(string key);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 轻量级流程控制器
|
||||
/// </summary>
|
||||
public class LightweightFlowControl : IFlowControl
|
||||
{
|
||||
private readonly Dictionary<string, CallTree> callTree = new Dictionary<string, CallTree>();
|
||||
private readonly IFlowCallTree flowCallTree;
|
||||
private readonly IFlowEnvironment flowEnvironment;
|
||||
|
||||
public LightweightFlowControl()
|
||||
public LightweightFlowControl(IFlowCallTree flowCallTree, IFlowEnvironment flowEnvironment)
|
||||
{
|
||||
this.flowCallTree = flowCallTree;
|
||||
this.flowEnvironment = flowEnvironment;
|
||||
}
|
||||
|
||||
public Task<object> InvokeAsync(string apiGuid, Dictionary<string, object> dict)
|
||||
@@ -334,9 +362,41 @@ namespace Serein.Library.FlowNode
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public Task<bool> StartFlowFromSelectNodeAsync(string startNodeGuid)
|
||||
|
||||
//private readonly DefaultObjectPool<IDynamicContext> _stackPool = new DefaultObjectPool<IDynamicContext>(new DynamicContext(this));
|
||||
|
||||
|
||||
public async Task<TResult> StartFlowAsync<TResult>(string startNodeGuid)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
IDynamicContext context = new DynamicContext(flowEnvironment);
|
||||
CancellationTokenSource cts = new CancellationTokenSource();
|
||||
#if DEBUG
|
||||
|
||||
FlowResult flowResult = await BenchmarkHelpers.BenchmarkAsync(async () =>
|
||||
{
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
var flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
return flowResult;
|
||||
});
|
||||
#else
|
||||
var node = flowCallTree.Get(startNodeGuid);
|
||||
FlowResult flowResult = await node.StartFlowAsync(context, cts.Token);
|
||||
#endif
|
||||
|
||||
cts?.Cancel();
|
||||
cts?.Dispose();
|
||||
if (flowResult.Value is TResult result)
|
||||
{
|
||||
return result;
|
||||
}
|
||||
else if (flowResult is FlowResult && flowResult is TResult result2)
|
||||
{
|
||||
return result2;
|
||||
}
|
||||
else
|
||||
{
|
||||
throw new ArgumentNullException($"类型转换失败,流程返回数据与泛型不匹配,当前返回类型为[{flowResult.Value.GetType().FullName}]。");
|
||||
}
|
||||
}
|
||||
public async Task<bool> StartFlowAsync(string[] canvasGuids)
|
||||
{
|
||||
@@ -501,6 +561,14 @@ namespace Serein.Library.FlowNode
|
||||
/// </summary>
|
||||
public class LightweightFlowEnvironment : IFlowEnvironment
|
||||
{
|
||||
|
||||
public LightweightFlowEnvironment(IFlowEnvironmentEvent lightweightFlowEnvironmentEvent)
|
||||
{
|
||||
this.Event = lightweightFlowEnvironmentEvent;
|
||||
}
|
||||
|
||||
|
||||
|
||||
public void WriteLine(InfoType type, string message, InfoClass @class = InfoClass.Trivial)
|
||||
{
|
||||
Console.WriteLine(message);
|
||||
@@ -513,7 +581,7 @@ namespace Serein.Library.FlowNode
|
||||
|
||||
public IFlowControl FlowControl => throw new NotImplementedException();
|
||||
|
||||
public IFlowEnvironmentEvent Event => throw new NotImplementedException();
|
||||
public IFlowEnvironmentEvent Event { get; private set; }
|
||||
|
||||
public string EnvName => throw new NotImplementedException();
|
||||
|
||||
|
||||
@@ -82,6 +82,12 @@ namespace Serein.Library
|
||||
[PropertyInfo]
|
||||
private int _paramsArgIndex = -1;
|
||||
|
||||
/// <summary>
|
||||
/// 是否为异步方法(如果为异步方法,则返回值类型为Task或Task<T>)
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private bool _isAsync = false;
|
||||
|
||||
/// <summary>
|
||||
/// 出参类型
|
||||
/// </summary>
|
||||
@@ -269,6 +275,7 @@ namespace Serein.Library
|
||||
ParameterDetailsInfos = this.ParameterDetailss?.Select(p => p.ToInfo()).ToArray(),
|
||||
ReturnTypeFullName = this.ReturnType?.FullName,
|
||||
IsParamsArgIndex = this.ParamsArgIndex,
|
||||
IsAsync = this.IsAsync,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -291,6 +298,7 @@ namespace Serein.Library
|
||||
MethodLockName = this.MethodLockName, // 拷贝
|
||||
ParamsArgIndex = this.ParamsArgIndex, // 拷贝
|
||||
ParameterDetailss = this.ParameterDetailss?.Select(p => p?.CloneOfModel(nodeModel)).ToArray(), // 拷贝属于节点方法的新入参描述
|
||||
IsAsync = this.IsAsync, // 拷贝
|
||||
};
|
||||
|
||||
return md;
|
||||
|
||||
@@ -38,10 +38,15 @@ namespace Serein.Library
|
||||
public ParameterDetailsInfo[] ParameterDetailsInfos { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 可选参数信息
|
||||
/// 可选参数信息(-1表示不存在)
|
||||
/// </summary>
|
||||
public int IsParamsArgIndex { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 是否为异步方法
|
||||
/// </summary>
|
||||
public bool IsAsync{ get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 出参类型
|
||||
/// </summary>
|
||||
|
||||
@@ -14,6 +14,9 @@
|
||||
<LangVersion>latest</LangVersion>
|
||||
<SatelliteResourceLanguages>no</SatelliteResourceLanguages>
|
||||
|
||||
|
||||
<Optimize>true</Optimize>
|
||||
|
||||
<GenerateDocumentationFile>true</GenerateDocumentationFile>
|
||||
<EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>
|
||||
<CompilerGeneratedFilesOutputPath>.\obj\g</CompilerGeneratedFilesOutputPath>
|
||||
|
||||
@@ -15,13 +15,13 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
|
||||
[DynamicFlow(Name ="[基础功能]")]
|
||||
public class SereinBaseFunction
|
||||
public static class SereinBaseFunction
|
||||
{
|
||||
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "键值对组装")]
|
||||
private Dictionary<string, object> SereinKvDataCollectionNode(string argName,
|
||||
public static Dictionary<string, object> SereinKvDataCollectionNode(string argName,
|
||||
params object[] value)
|
||||
{
|
||||
|
||||
@@ -36,13 +36,13 @@ namespace Serein.Library
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "数组组装")]
|
||||
private object[] SereinListDataCollectionNode(params object[] value)
|
||||
public static object[] SereinListDataCollectionNode(params object[] value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "输出")]
|
||||
private object[] SereinConsoleNode(params object[] value)
|
||||
public static object[] SereinConsoleNode(params object[] value)
|
||||
{
|
||||
foreach (var item in value)
|
||||
{
|
||||
@@ -52,7 +52,7 @@ namespace Serein.Library
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "逻辑分支")]
|
||||
private object SereinLogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
|
||||
public static object SereinLogicalBranch([NodeParam(IsExplicit = false)]bool @bool,
|
||||
object t_value,
|
||||
object f_value)
|
||||
{
|
||||
@@ -60,7 +60,7 @@ namespace Serein.Library
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "文本拼接")]
|
||||
private string SereinTextJoin(params object[] value)
|
||||
public static string SereinTextJoin(params object[] value)
|
||||
{
|
||||
StringBuilder sb = new StringBuilder();
|
||||
foreach (var item in value)
|
||||
@@ -84,7 +84,7 @@ namespace Serein.Library
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "键值对动态构建对象")]
|
||||
private object SereinKvDataToObject(Dictionary<string, object> dict,
|
||||
public static object SereinKvDataToObject(Dictionary<string, object> dict,
|
||||
string classTypeName = "newClass_dynamic",
|
||||
bool IsPrint = false)
|
||||
{
|
||||
@@ -105,7 +105,7 @@ namespace Serein.Library
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "设置/更新全局数据")]
|
||||
private object SereinAddOrUpdateFlowGlobalData(string name, object data)
|
||||
public static object SereinAddOrUpdateFlowGlobalData(string name, object data)
|
||||
{
|
||||
SereinEnv.AddOrUpdateFlowGlobalData(name, data);
|
||||
return data;
|
||||
|
||||
133
Library/Utils/BenchmarkHelpers.cs
Normal file
133
Library/Utils/BenchmarkHelpers.cs
Normal file
@@ -0,0 +1,133 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
/// <summary>
|
||||
/// 代码计时工具类
|
||||
/// </summary>
|
||||
public static class BenchmarkHelpers
|
||||
{
|
||||
/// <summary>
|
||||
/// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
|
||||
/// </summary>
|
||||
/// <param name="func">需要执行的异步方法</param>
|
||||
/// <param name="count">执行次数,默认10000</param>
|
||||
public static void Benchmark(Action action, int count = 10000)
|
||||
{
|
||||
double max = double.MinValue;
|
||||
double min = double.MaxValue;
|
||||
double total = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
action();
|
||||
sw.Stop();
|
||||
|
||||
double ms = sw.Elapsed.TotalMilliseconds;
|
||||
if (ms > max) max = ms;
|
||||
if (ms < min) min = ms;
|
||||
total += ms;
|
||||
}
|
||||
|
||||
double avg = total / count;
|
||||
|
||||
Console.WriteLine($"运行 {count} 次:");
|
||||
Console.WriteLine($"最大耗时:{max} 毫秒");
|
||||
Console.WriteLine($"最小耗时:{min} 毫秒");
|
||||
Console.WriteLine($"平均耗时:{avg} 毫秒");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
|
||||
/// </summary>
|
||||
/// <param name="func">需要执行的异步方法</param>
|
||||
/// <param name="count">执行次数,默认10000</param>
|
||||
public static async Task BenchmarkAsync(Func<Task> func, int count = 10000)
|
||||
{
|
||||
double max = double.MinValue;
|
||||
double min = double.MaxValue;
|
||||
double total = 0;
|
||||
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
await func();
|
||||
sw.Stop();
|
||||
|
||||
double ms = sw.Elapsed.TotalMilliseconds;
|
||||
if (ms > max) max = ms;
|
||||
if (ms < min) min = ms;
|
||||
total += ms;
|
||||
}
|
||||
|
||||
double avg = total / count;
|
||||
Console.WriteLine($"运行 {count} 次:");
|
||||
Console.WriteLine($"总耗时 :{total} 毫秒:");
|
||||
Console.WriteLine($"最大耗时:{max} 毫秒");
|
||||
Console.WriteLine($"最小耗时:{min} 毫秒");
|
||||
Console.WriteLine($"平均耗时:{avg} 毫秒");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
|
||||
/// </summary>
|
||||
/// <param name="func">需要执行的异步方法</param>
|
||||
/// <param name="count">执行次数,默认10000</param>
|
||||
public static async Task<TReult> BenchmarkAsync<TReult>(Func<Task<TReult>> func, int count = 10000)
|
||||
{
|
||||
double max = double.MinValue;
|
||||
double min = double.MaxValue;
|
||||
double total = 0;
|
||||
TReult result = default;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
var sw = Stopwatch.StartNew();
|
||||
result = await func();
|
||||
sw.Stop();
|
||||
|
||||
double ms = sw.Elapsed.TotalMilliseconds;
|
||||
if (ms > max) max = ms;
|
||||
if (ms < min) min = ms;
|
||||
total += ms;
|
||||
//Console.WriteLine($"第{count}次: 耗时 {ms} ms");
|
||||
}
|
||||
|
||||
double avg = total / count;
|
||||
Console.WriteLine($"运行 {count} 次:");
|
||||
Console.WriteLine($"总耗时 :{total} 毫秒:");
|
||||
Console.WriteLine($"最大耗时:{max} 毫秒");
|
||||
Console.WriteLine($"最小耗时:{min} 毫秒");
|
||||
Console.WriteLine($"平均耗时:{avg} 毫秒");
|
||||
return result;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 运行指定异步方法多次并输出耗时的最大、最小和平均值。
|
||||
/// </summary>
|
||||
/// <param name="func">需要执行的异步方法</param>
|
||||
public static async Task<TReult> BenchmarkAsync<TReult>(Func<Task<TReult>> func)
|
||||
{
|
||||
double max = double.MinValue;
|
||||
double min = double.MaxValue;
|
||||
double total = 0;
|
||||
TReult result = default;
|
||||
var sw = Stopwatch.StartNew();
|
||||
result = await func();
|
||||
sw.Stop();
|
||||
|
||||
double ms = sw.Elapsed.TotalMilliseconds;
|
||||
if (ms > max) max = ms;
|
||||
if (ms < min) min = ms;
|
||||
total += ms;
|
||||
|
||||
Console.WriteLine($"运行1次耗时 :{total} 毫秒:");
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -31,6 +31,7 @@ namespace Serein.Library.Utils
|
||||
/// </summary>
|
||||
private readonly ConcurrentDictionary<string, Func<object>> _registerCallback;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 未完成注入的实例集合。
|
||||
/// 键:需要的类型名称
|
||||
@@ -83,7 +84,6 @@ namespace Serein.Library.Utils
|
||||
/// 向容器注册类型,并指定其实例成员
|
||||
/// </summary>
|
||||
/// <typeparam name="T">需要注册的类型</typeparam>
|
||||
/// <param name="getInstance">获取实例的回调函数</param>
|
||||
/// <returns></returns>
|
||||
public ISereinIOC Register<T>()
|
||||
{
|
||||
@@ -117,6 +117,7 @@ namespace Serein.Library.Utils
|
||||
public ISereinIOC Register<TService, TImplementation>(Func<TService> getInstance)
|
||||
where TImplementation : TService
|
||||
{
|
||||
|
||||
RegisterType(typeof(TService).FullName, typeof(TImplementation), () => getInstance.Invoke());
|
||||
return this;
|
||||
}
|
||||
@@ -130,6 +131,7 @@ namespace Serein.Library.Utils
|
||||
public ISereinIOC Register<TService, TImplementation>()
|
||||
where TImplementation : TService
|
||||
{
|
||||
|
||||
RegisterType(typeof(TService).FullName, typeof(TImplementation));
|
||||
return this;
|
||||
}
|
||||
@@ -137,7 +139,7 @@ namespace Serein.Library.Utils
|
||||
|
||||
#endregion
|
||||
|
||||
#region 示例的创建
|
||||
#region 实例的创建
|
||||
/// <summary>
|
||||
/// 用于临时实例的创建,不登记到IOC容器中,依赖项注入失败时也不记录。
|
||||
/// </summary>
|
||||
@@ -335,7 +337,8 @@ namespace Serein.Library.Utils
|
||||
{
|
||||
if (!dependencyMap[IOC_MAIN].Contains(type.FullName))
|
||||
{
|
||||
dependencyMap[IOC_MAIN].Add(type.FullName);
|
||||
//dependencyMap[IOC_MAIN].Add(type.FullName);
|
||||
dependencyMap[IOC_MAIN].Add(typeFullName);
|
||||
}
|
||||
continue;
|
||||
}
|
||||
@@ -459,6 +462,7 @@ namespace Serein.Library.Utils
|
||||
/// <returns></returns>
|
||||
private object CreateInstance(string typeName)
|
||||
{
|
||||
|
||||
if (!_typeMappings.TryGetValue(typeName, out var type)) // 获取类型
|
||||
{
|
||||
return null;
|
||||
@@ -535,8 +539,8 @@ namespace Serein.Library.Utils
|
||||
/// <returns></returns>
|
||||
public ISereinIOC Build()
|
||||
{
|
||||
var dependencyTree = BuildDependencyTree(); // 生成类型依赖关系
|
||||
var creationOrder = GetCreationOrder(dependencyTree); // 生成创建顺序
|
||||
Dictionary<string, List<string>> dependencyTree = BuildDependencyTree(); // 生成类型依赖关系
|
||||
List<string> creationOrder = GetCreationOrder(dependencyTree); // 生成创建顺序
|
||||
|
||||
// 输出创建顺序
|
||||
Debug.WriteLine("创建顺序: " + string.Join($"{Environment.NewLine}↓ {Environment.NewLine}", creationOrder));
|
||||
@@ -544,7 +548,10 @@ namespace Serein.Library.Utils
|
||||
// 创建对象
|
||||
foreach (var typeName in creationOrder)
|
||||
{
|
||||
|
||||
if (typeName.Equals("Serein.Library.LightweightFlowEnvironment"))
|
||||
{
|
||||
|
||||
}
|
||||
if (_dependencies.ContainsKey(typeName))
|
||||
{
|
||||
continue;
|
||||
@@ -554,13 +561,15 @@ namespace Serein.Library.Utils
|
||||
continue;
|
||||
}
|
||||
var value = CreateInstance(typeName);
|
||||
if(value is null)
|
||||
|
||||
if (value is null)
|
||||
{
|
||||
SereinEnv.WriteLine(InfoType.ERROR, $"IOC容器无法创建对象:{typeName}");
|
||||
continue;
|
||||
}
|
||||
_dependencies[typeName] = value;
|
||||
OnIOCMembersChanged?.Invoke(new IOCMembersChangedEventArgs(typeName, value));
|
||||
|
||||
}
|
||||
_typeMappings.Clear();
|
||||
return this;
|
||||
|
||||
Reference in New Issue
Block a user