using Newtonsoft.Json; using Serein.Library; using Serein.Library.Api; using Serein.Library.Core; using Serein.Library.FlowNode; using Serein.Library.Utils; using Serein.Library.Utils.SereinExpression; using Serein.NodeFlow.Model; using Serein.NodeFlow.Tool; using System.Collections.Concurrent; using System.Numerics; using System.Reflection; using System.Reflection.Metadata.Ecma335; using System.Runtime.Loader; using System.Xml.Linq; using static Serein.Library.Utils.ChannelFlowInterrupt; namespace Serein.NodeFlow.Env { /// /// 运行环境 /// public class FlowEnvironment : IFlowEnvironment, ISereinIOC { /// /// 节点的命名空间 /// public const string SpaceName = $"{nameof(Serein)}.{nameof(NodeFlow)}.{nameof(Model)}"; public const string ThemeKey = "theme"; public const string DataKey = "data"; public const string MsgIdKey = "msgid"; /// /// 流程运行环境 /// public FlowEnvironment(UIContextOperation uiContextOperation) { this.sereinIOC = new SereinIOC(); this.ChannelFlowInterrupt = new ChannelFlowInterrupt(); this.IsGlobalInterrupt = false; this.flowStarter = null; this.sereinIOC.OnIOCMembersChanged += e => { if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => this?.OnIOCMembersChanged?.Invoke(e)); // 监听IOC容器的注册 } }; this.UIContextOperation = uiContextOperation; // 本地环境需要存放视图管理 } #region 远程管理 private MsgControllerOfServer clientMsgManage; /// /// 表示是否正在控制远程 /// Local control remote env /// public bool IsControlRemoteEnv { get; set; } /// /// 打开远程管理 /// /// public async Task StartRemoteServerAsync(int port = 7525) { if (clientMsgManage is null) { clientMsgManage = new MsgControllerOfServer(this); //clientMsgManage = new MsgControllerOfServer(this,"123456"); } _ = clientMsgManage.StartRemoteServerAsync(port); } /// /// 结束远程管理 /// public void StopRemoteServer() { try { clientMsgManage.StopRemoteServer(); } catch (Exception ex) { Console.WriteLine("结束远程管理异常:" + ex); } } #endregion #region 环境运行事件 /// /// 加载Dll /// public event LoadDllHandler? OnDllLoad; /// /// 移除DLL /// public event RemoteDllHandler? OnDllRemote; /// /// 项目加载完成 /// public event ProjectLoadedHandler? OnProjectLoaded; /// /// 节点连接属性改变事件 /// public event NodeConnectChangeHandler? OnNodeConnectChange; /// /// 节点创建事件 /// public event NodeCreateHandler? OnNodeCreate; /// /// 移除节点事件 /// public event NodeRemoveHandler? OnNodeRemove; /// /// 起始节点变化事件 /// public event StartNodeChangeHandler? OnStartNodeChange; /// /// 流程运行完成事件 /// public event FlowRunCompleteHandler? OnFlowRunComplete; /// /// 被监视的对象改变事件 /// public event MonitorObjectChangeHandler? OnMonitorObjectChange; /// /// 节点中断状态改变事件 /// public event NodeInterruptStateChangeHandler? OnNodeInterruptStateChange; /// /// 节点触发了中断 /// public event ExpInterruptTriggerHandler? OnInterruptTrigger; /// /// 容器改变 /// public event IOCMembersChangedHandler? OnIOCMembersChanged; /// /// 节点需要定位 /// public event NodeLocatedHandler? OnNodeLocated; /// /// 节点移动了(远程插件) /// public event NodeMovedHandler? OnNodeMoved; /// /// 运行环境输出 /// public event EnvOutHandler? OnEnvOut; #endregion #region 属性 /// /// 当前环境 /// public IFlowEnvironment CurrentEnv { get => this; } /// /// UI线程操作类 /// public UIContextOperation UIContextOperation { get; set; } /// /// 如果没有全局触发器,且没有循环分支,流程执行完成后自动为 Completion 。 /// public RunState FlowState { get; set; } = RunState.NoStart; /// /// 如果全局触发器还在运行,则为 Running 。 /// public RunState FlipFlopState { get; set; } = RunState.NoStart; /// /// 环境名称 /// public string EnvName { get; set; } = SpaceName; /// /// 是否全局中断 /// public bool IsGlobalInterrupt { get; set; } /// /// 流程中断器 /// public ChannelFlowInterrupt ChannelFlowInterrupt { get; set; } /// /// 单例模式IOC容器,内部维护了一个实例字典,默认使用类型的FullName作为Key,如果以“接口-实现类”的方式注册,那么将使用接口类型的FullName作为Key。 /// 当某个类型注册绑定成功后,将不会因为其它地方尝试注册相同类型的行为导致类型被重新创建。 /// public ISereinIOC IOC { get => this; } #endregion #region 私有变量 /// /// Library 与 MethodDetailss的依赖关系 /// public ConcurrentDictionary> MethodDetailsOfLibrarys { get; } = []; /// /// 存储已加载的程序集 /// public ConcurrentDictionary Librarys { get; } = []; /// /// 存储已加载的方法信息。描述所有DLL中NodeAction特性的方法的原始副本 /// public ConcurrentDictionary MethodDetailss { get; } = []; /// /// 容器管理 /// private readonly SereinIOC sereinIOC; /// /// 环境加载的节点集合 /// Node Guid - Node Model /// private Dictionary NodeModels { get; } = []; /// /// 存放触发器节点(运行时全部调用) /// private List FlipflopNodes { get; } = []; /// /// 从dll中加载的类的注册类型 /// private Dictionary> AutoRegisterTypes { get; } = []; /// /// 存放委托 /// /// md.Methodname - delegate /// private ConcurrentDictionary MethodDelegates { get; } = []; /// /// 起始节点私有属性 /// private NodeModelBase? _startNode = null; /// /// 起始节点 /// private NodeModelBase? StartNode { get { return _startNode; } set { if (value is null) { return; } if (_startNode is not null) { _startNode.IsStart = false; } value.IsStart = true; _startNode = value; } } /// /// 流程启动器(每次运行时都会重新new一个) /// private FlowStarter? flowStarter; #endregion #region 环境对外接口 /// /// 重定向Console输出 /// public void SetConsoleOut() { var logTextWriter = new LogTextWriter(msg => Output(msg)); Console.SetOut(logTextWriter); } /// /// 使用JSON处理库输出对象信息 /// /// public void WriteLineObjToJson(object obj) { var msg = JsonConvert.SerializeObject(obj); if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg + Environment.NewLine)); } } /// /// 异步运行 /// /// public async Task StartAsync() { ChannelFlowInterrupt?.CancelAllTasks(); flowStarter = new FlowStarter(); var nodes = NodeModels.Values.ToList(); List initMethods = []; List loadMethods = []; List exitMethods = []; var initMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Init); var loadMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Loading); var exitMds = MethodDetailss.Values.Where(it => it.MethodDynamicType == NodeType.Exit); initMethods.AddRange(initMds); loadMethods.AddRange(loadMds); exitMethods.AddRange(exitMds); IOC.Reset(); // 开始运行时清空ioc中注册的实例 IOC.CustomRegisterInstance(typeof(IFlowEnvironment).FullName, this); if (this.UIContextOperation is not null) IOC.CustomRegisterInstance(typeof(UIContextOperation).FullName, this.UIContextOperation, false); await flowStarter.RunAsync(this, nodes, AutoRegisterTypes, initMethods, loadMethods, exitMethods); if (FlipFlopState == RunState.Completion) { ExitFlow(); // 未运行触发器时,才会调用结束方法 } } /// /// 从选定节点开始运行 /// /// /// public async Task StartAsyncInSelectNode(string startNodeGuid) { if (flowStarter is null) { return; } if (true || FlowState == RunState.Running || FlipFlopState == RunState.Running) { NodeModelBase? nodeModel = GuidToModel(startNodeGuid); if (nodeModel is null || nodeModel is SingleFlipflopNode) { return; } //var getExp = "@get .DebugSetting.IsEnable"; //var getExpResult1 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel,out _); //var setExp = "@set .DebugSetting.IsEnable = false"; //SerinExpressionEvaluator.Evaluate(setExp, nodeModel,out _); //var getExpResult2 = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); await flowStarter.StartFlowInSelectNodeAsync(this, nodeModel); } else { return; } } /// /// 单独运行一个节点 /// /// /// public async Task InvokeNodeAsync(IDynamicContext context, string nodeGuid) { object result = true; if (this.NodeModels.TryGetValue(nodeGuid, out var model)) { result = await model.ExecutingAsync(context); } return result; } /// /// 结束流程 /// public void ExitFlow() { ChannelFlowInterrupt?.CancelAllTasks(); flowStarter?.Exit(); UIContextOperation?.Invoke(() => OnFlowRunComplete?.Invoke(new FlowEventArgs())); flowStarter = null; GC.Collect(); } /// /// 激活全局触发器 /// /// // [AutoSocketHandle] public void ActivateFlipflopNode(string nodeGuid) { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return; if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 { if (FlowState != RunState.Completion && flipflopNode.NotExitPreviousNode()) // 正在运行,且该触发器没有上游节点 { _ = flowStarter.RunGlobalFlipflopAsync(this, flipflopNode);// 被父节点移除连接关系的子节点若为触发器,且无上级节点,则当前流程正在运行,则加载到运行环境中 } } } /// /// 关闭全局触发器 /// /// // [AutoSocketHandle] public void TerminateFlipflopNode(string nodeGuid) { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return; if (flowStarter is not null && nodeModel is SingleFlipflopNode flipflopNode) // 子节点为触发器 { flowStarter.TerminateGlobalFlipflopRuning(flipflopNode); } } /// /// 获取当前环境信息(远程连接) /// /// public async Task GetEnvInfoAsync() { Dictionary> LibraryMds = []; foreach (var mdskv in MethodDetailsOfLibrarys) { var library = mdskv.Key; var mds = mdskv.Value; foreach (var md in mds) { if (!LibraryMds.TryGetValue(library, out var t_mds)) { t_mds = new List(); LibraryMds[library] = t_mds; } var mdInfo = md.ToInfo(); mdInfo.LibraryName = library.FullName; t_mds.Add(mdInfo); } } LibraryMds[] libraryMdss = LibraryMds.Select(kv => new LibraryMds { LibraryName = kv.Key.FullName, Mds = kv.Value.ToArray() }).ToArray(); var project = await GetProjectInfoAsync(); Console.WriteLine("已将当前环境信息发送到远程客户端"); return new FlowEnvInfo { Project = project, // 项目信息 LibraryMds = libraryMdss, // 环境方法 }; } /// /// 清除所有 /// public void ClearAll() { //LoadedAssemblyPaths.Clear(); //NodeLibrarys.Clear(); //MethodDetailss.Clear(); } /// /// 加载项目文件 /// /// 环境信息 /// public void LoadProject(FlowEnvInfo flowEnvInfo, string filePath) { var projectData = flowEnvInfo.Project; // 加载项目配置文件 var dllPaths = projectData.Librarys.Select(it => it.FileName).ToList(); List methodDetailss = []; // 遍历依赖项中的特性注解,生成方法详情 foreach (var dllPath in dllPaths) { var dllFilePath = Path.GetFullPath(Path.Combine(filePath, dllPath)); LoadDllNodeInfo(dllFilePath); } List<(NodeModelBase, string[])> regionChildNodes = new List<(NodeModelBase, string[])>(); List<(NodeModelBase, PositionOfUI)> ordinaryNodes = new List<(NodeModelBase, PositionOfUI)>(); // 加载节点 foreach (var nodeInfo in projectData.Nodes) { var controlType = FlowFunc.GetNodeControlType(nodeInfo); if (controlType == NodeControlType.None) { continue; } else { MethodDetails? methodDetails = null; if (!string.IsNullOrEmpty(nodeInfo.MethodName)) { MethodDetailss.TryGetValue(nodeInfo.MethodName, out methodDetails);// 加载项目时尝试获取方法信息 } var nodeModel = FlowFunc.CreateNode(this, controlType, methodDetails); // 加载项目时创建节点 nodeModel.LoadInfo(nodeInfo); // 创建节点model if (nodeModel is null) { nodeInfo.Guid = string.Empty; continue; } TryAddNode(nodeModel); // 加载项目时将节点加载到环境中 if (nodeInfo.ChildNodeGuids?.Length > 0) { regionChildNodes.Add((nodeModel, nodeInfo.ChildNodeGuids)); UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, nodeInfo.Position))); } else { ordinaryNodes.Add((nodeModel, nodeInfo.Position)); } } } // 加载区域子项 foreach ((NodeModelBase region, string[] childNodeGuids) item in regionChildNodes) { foreach (var childNodeGuid in item.childNodeGuids) { NodeModels.TryGetValue(childNodeGuid, out NodeModelBase? childNode); if (childNode is null) { // 节点尚未加载 continue; } UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(childNode, true, item.region.Guid))); // 存在节点 } } // 加载节点 foreach ((NodeModelBase nodeModel, PositionOfUI position) item in ordinaryNodes) { bool IsContinue = false; foreach ((NodeModelBase region, string[] childNodeGuids) item2 in regionChildNodes) { foreach (var childNodeGuid in item2.childNodeGuids) { if (item.nodeModel.Guid.Equals(childNodeGuid)) { IsContinue = true; } } } if (IsContinue) continue; UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(item.nodeModel, item.position))); } // 确定节点之间的连接关系 Task.Run(async () => { await Task.Delay(777); #region 方法调用关系 foreach (var nodeInfo in projectData.Nodes) { if (!NodeModels.TryGetValue(nodeInfo.Guid, out NodeModelBase? fromNode)) { // 不存在对应的起始节点 continue; } List<(ConnectionInvokeType connectionType, string[] guids)> allToNodes = [(ConnectionInvokeType.IsSucceed,nodeInfo.TrueNodes), (ConnectionInvokeType.IsFail, nodeInfo.FalseNodes), (ConnectionInvokeType.IsError, nodeInfo.ErrorNodes), (ConnectionInvokeType.Upstream, nodeInfo.UpstreamNodes)]; List<(ConnectionInvokeType, NodeModelBase[])> fromNodes = allToNodes.Where(info => info.guids.Length > 0) .Select(info => (info.connectionType, info.guids.Where(guid => NodeModels.ContainsKey(guid)).Select(guid => NodeModels[guid]) .ToArray())) .ToList(); // 遍历每种类型的节点分支(四种) foreach ((ConnectionInvokeType connectionType, NodeModelBase[] toNodes) item in fromNodes) { // 遍历当前类型分支的节点(确认连接关系) foreach (var toNode in item.toNodes) { _ = ConnectInvokeOfNode(fromNode, toNode, item.connectionType); // 加载时确定节点间的连接关系 } } } #endregion #region 参数调用关系 foreach (var toNode in NodeModels.Values) { for (var i = 0; i < toNode.MethodDetails.ParameterDetailss.Length; i++) { var pd = toNode.MethodDetails.ParameterDetailss[i]; if (!string.IsNullOrEmpty(pd.ArgDataSourceNodeGuid) && NodeModels.TryGetValue(pd.ArgDataSourceNodeGuid, out var fromNode)) { await ConnectArgSourceOfNodeAsync(fromNode, toNode, pd.ArgDataSourceType, pd.Index); } } } #endregion }); SetStartNode(projectData.StartNode); UIContextOperation?.Invoke(() => OnProjectLoaded?.Invoke(new ProjectLoadedEventArgs())); } /// /// 加载远程环境 /// /// 远程环境地址 /// 远程环境端口 /// 密码 public async Task<(bool, RemoteMsgUtil)> ConnectRemoteEnv(string addres, int port, string token) { if (IsControlRemoteEnv) { await Console.Out.WriteLineAsync($"当前已经连接远程环境"); return (false, null); } // 没有连接远程环境,可以重新连接 var controlConfiguration = new RemoteMsgUtil.ControlConfiguration { Addres = addres, Port = port, Token = token, ThemeJsonKey = FlowEnvironment.ThemeKey, MsgIdJsonKey = FlowEnvironment.MsgIdKey, DataJsonKey = FlowEnvironment.DataKey, }; var remoteMsgUtil = new RemoteMsgUtil(controlConfiguration); var result = await remoteMsgUtil.ConnectAsync(); if (!result) { await Console.Out.WriteLineAsync("连接失败,请检查地址与端口是否正确"); return (false, null); } await Console.Out.WriteLineAsync("连接成功,开始验证Token"); IsControlRemoteEnv = true; return (true, remoteMsgUtil); } /// /// 退出远程环境 /// public void ExitRemoteEnv() { IsControlRemoteEnv = false; } /// /// 序列化当前项目的依赖信息、节点信息 /// /// public Task GetProjectInfoAsync() { var projectData = new SereinProjectData() { Librarys = Librarys.Values.Select(lib => lib.ToLibrary()).ToArray(), Nodes = NodeModels.Values.Select(node => node.ToInfo()).Where(info => info is not null).ToArray(), StartNode = NodeModels.Values.FirstOrDefault(it => it.IsStart)?.Guid, }; return Task.FromResult(projectData); } /// /// 从文件路径中加载DLL /// /// /// // [AutoSocketHandle] public void LoadDll(string dllPath) { LoadDllNodeInfo(dllPath); } /// /// 移除DLL /// /// /// public bool RemoteDll(string assemblyFullName) { var library = Librarys.Values.FirstOrDefault(nl => assemblyFullName.Equals(nl.FullName)); if (library is null) { return false; } var groupedNodes = NodeModels.Values .Where(node => node.MethodDetails is not null) .ToArray() .GroupBy(node => node.MethodDetails?.MethodName) .ToDictionary( key => key.Key, group => group.Count()); if (NodeModels.Count == 0) { return true; // 当前无节点,可以直接删除 } if (MethodDetailsOfLibrarys.TryGetValue(library, out var mds)) // 存在方法 { foreach (var md in mds) { if (groupedNodes.TryGetValue(md.MethodName, out int count)) { if (count > 0) { return false; // 创建过相关的节点,无法移除 } } } // 开始移除相关信息 foreach (var md in mds) { MethodDetailss.TryRemove(md.MethodName, out _); } MethodDetailsOfLibrarys.TryRemove(library, out _); return true; } else { return true; } } /// /// 流程正在运行时创建节点 /// /// /// /// 如果是表达式节点条件节点,该项为null public Task CreateNodeAsync(NodeControlType nodeControlType, PositionOfUI position, MethodDetailsInfo? methodDetailsInfo = null) { NodeModelBase? nodeModel; if (methodDetailsInfo is null) { nodeModel = FlowFunc.CreateNode(this, nodeControlType); // 加载基础节点 } else { if (MethodDetailss.TryGetValue(methodDetailsInfo.MethodName, out var methodDetails)) { nodeModel = FlowFunc.CreateNode(this, nodeControlType, methodDetails); // 一般的加载节点方法 } else { return Task.FromResult(null); } } TryAddNode(nodeModel); nodeModel.Position = position; // 通知UI更改 UIContextOperation?.Invoke(() => OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeModel, position))); // 因为需要UI先布置了元素,才能通知UI变更特效 // 如果不存在流程起始控件,默认设置为流程起始控件 if (StartNode is null) { SetStartNode(nodeModel); } var nodeInfo = nodeModel.ToInfo(); return Task.FromResult(nodeInfo); ; } /// /// 移除节点 /// /// /// public async Task RemoveNodeAsync(string nodeGuid) { var remoteNode = GuidToModel(nodeGuid); if (remoteNode is null) return false; //if (remoteNode.IsStart) //{ // return; //} if (remoteNode is SingleFlipflopNode flipflopNode) { flowStarter?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被移除的是全局触发器,尝试从启动器移除 } // 遍历所有父节点,从那些父节点中的子节点集合移除该节点 foreach (var pnc in remoteNode.PreviousNodes) { var pCType = pnc.Key; // 连接类型 for (int i = 0; i < pnc.Value.Count; i++) { NodeModelBase? pNode = pnc.Value[i]; pNode.SuccessorNodes[pCType].Remove(remoteNode); UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( pNode.Guid, remoteNode.Guid, JunctionOfConnectionType.Invoke, pCType, // 对应的连接关系 NodeConnectChangeEventArgs.ConnectChangeType.Remote))); // 通知UI } } // 遍历所有子节点,从那些子节点中的父节点集合移除该节点 foreach (var snc in remoteNode.SuccessorNodes) { var connectionType = snc.Key; // 连接类型 for (int i = 0; i < snc.Value.Count; i++) { NodeModelBase? toNode = snc.Value[i]; await RemoteConnectAsync(remoteNode, toNode, connectionType); } } // 从集合中移除节点 NodeModels.Remove(nodeGuid); UIContextOperation?.Invoke(() => OnNodeRemove?.Invoke(new NodeRemoveEventArgs(nodeGuid))); return true; } /// /// 连接节点,创建方法调用关系 /// /// 起始节点 /// 目标节点 /// 起始节点控制点 /// 目标节点控制点 /// 连接关系 public async Task ConnectInvokeNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionInvokeType invokeType) { // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); if (fromNode is null || toNode is null) return false; (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); if (!state) { Console.WriteLine("出现非预期的连接行为"); return false; // 出现不符预期的连接行为,忽略此次连接行为 } if(type == JunctionOfConnectionType.Invoke) { if (fromNodeJunctionType == JunctionType.Execute) { // 如果 起始控制点 是“方法调用”,需要反转 from to 节点 (fromNode, toNode) = (toNode, fromNode); } // 从起始节点“下一个方法”控制点,连接到目标节点“方法调用”控制点 state = ConnectInvokeOfNode(fromNode, toNode, invokeType); // 本地环境进行连接 } return state; } /// /// 创建节点之间的参数来源关系 /// /// 起始节点 /// 目标节点 /// 起始节点控制点(result控制点) /// 目标节点控制点(argData控制点) /// 目标节点的第几个参数 /// 调用目标节点对应方法时,对应参数来源类型 /// public async Task ConnectArgSourceNodeAsync(string fromNodeGuid, string toNodeGuid, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType, ConnectionArgSourceType connectionArgSourceType, int argIndex) { // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); if (fromNode is null || toNode is null) return false; (var type, var state) = CheckConnect(fromNode, toNode, fromNodeJunctionType, toNodeJunctionType); if (!state) { Console.WriteLine("出现非预期的连接行为"); return false; // 出现不符预期的连接行为,忽略此次连接行为 } if (type == JunctionOfConnectionType.Arg) { // 从起始节点“返回值”控制点,连接到目标节点“方法入参”控制点 if (fromNodeJunctionType == JunctionType.ArgData) { // 如果 起始控制点 是“方法入参”,需要反转 from to 节点 (fromNode, toNode) = (toNode, fromNode); } // 确定方法入参关系 state = await ConnectArgSourceOfNodeAsync(fromNode, toNode, connectionArgSourceType, argIndex); // 本地环境进行连接 } return state; } /// /// 移除连接节点之间方法调用的关系 /// /// 起始节点Guid /// 目标节点Guid /// 连接关系 /// public async Task RemoveConnectInvokeAsync(string fromNodeGuid, string toNodeGuid, ConnectionInvokeType connectionType) { // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); if (fromNode is null || toNode is null) return false; var result = await RemoteConnectAsync(fromNode, toNode, connectionType); return result; } /// /// 移除连接节点之间参数传递的关系 /// /// 起始节点Guid /// 目标节点Guid /// 连接到第几个参数 public async Task RemoveConnectArgSourceAsync(string fromNodeGuid, string toNodeGuid, int argIndex) { // 获取起始节点与目标节点 var fromNode = GuidToModel(fromNodeGuid); var toNode = GuidToModel(toNodeGuid); if (fromNode is null || toNode is null) return false; var result = await RemoteConnectAsync(fromNode, toNode, argIndex); return result; } /// /// 获取方法描述 /// public bool TryGetMethodDetailsInfo(string name, out MethodDetailsInfo? md) { if (!string.IsNullOrEmpty(name)) { foreach (var t_md in MethodDetailss.Values) { md = t_md.ToInfo(); if (md != null) { return true; } } md = null; return false; } else { md = null; return false; } } /// /// 通过方法名称获取对应的Emit委托 /// 方法无入参时需要传入空数组,void方法自动返回null /// 普通方法:Func<object,object[],object> /// 异步方法:Func<object,object[],Task> /// 异步有返回值方法:Func<object,object[],Task<object>> /// /// /// /// public bool TryGetDelegateDetails(string methodName, out DelegateDetails? delegateDetails) { if (!string.IsNullOrEmpty(methodName) && MethodDelegates.TryGetValue(methodName, out delegateDetails)) { return delegateDetails != null; } else { delegateDetails = null; return false; } } /// /// 移动了某个节点(远程插件使用) /// /// /// /// public void MoveNode(string nodeGuid, double x, double y) { NodeModelBase? nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return; nodeModel.Position.X = x; nodeModel.Position.Y = y; UIContextOperation?.Invoke(() => OnNodeMoved?.Invoke(new NodeMovedEventArgs(nodeGuid, x, y))); } /// /// 设置起点控件 /// /// public void SetStartNode(string newNodeGuid) { var newStartNodeModel = GuidToModel(newNodeGuid); if (newStartNodeModel is null) return; SetStartNode(newStartNodeModel); } /// /// 中断指定节点,并指定中断等级。 /// /// 被中断的目标节点Guid /// 中断级别 /// 操作是否成功 public Task SetNodeInterruptAsync(string nodeGuid, bool isInterrupt) { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return Task.FromResult(false); if (!isInterrupt) { nodeModel.CancelInterrupt(); } else if (isInterrupt) { nodeModel.DebugSetting.CancelInterruptCallback?.Invoke(); nodeModel.DebugSetting.GetInterruptTask = async () => { TriggerInterrupt(nodeGuid, "", InterruptTriggerEventArgs.InterruptTriggerType.Monitor); var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(nodeGuid); return result; }; nodeModel.DebugSetting.CancelInterruptCallback = () => { //nodeModel.DebugSetting.IsInterrupt = false; ChannelFlowInterrupt.TriggerSignal(nodeGuid); }; } //nodeModel.DebugSetting.IsInterrupt = true; if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnNodeInterruptStateChange?.Invoke(new NodeInterruptStateChangeEventArgs(nodeGuid, isInterrupt))); } return Task.FromResult(true); } /// /// 添加表达式中断 /// /// 如果是节点,传入Guid;如果是对象,传入类型FullName /// 合法的条件表达式 /// public Task AddInterruptExpressionAsync(string key, string expression) { if (string.IsNullOrEmpty(expression)) return Task.FromResult(false); if (dictMonitorObjExpInterrupt.TryGetValue(key, out var condition)) { condition.Clear(); // 暂时 condition.Add(expression);// 暂时 } else { var exps = new List(); exps.Add(expression); dictMonitorObjExpInterrupt.TryAdd(key, exps); } return Task.FromResult(true); } /// /// 要监视的对象,以及与其关联的表达式 /// private ConcurrentDictionary> dictMonitorObjExpInterrupt = []; /// /// 设置对象的监视状态 /// /// 如果是节点,传入Guid;如果是对象,传入类型FullName /// ture监视对象;false取消对象监视 /// public void SetMonitorObjState(string key, bool isMonitor) { if (string.IsNullOrEmpty(key)) { return; } var isExist = dictMonitorObjExpInterrupt.ContainsKey(key); if (isExist) { if (!isMonitor) // 对象存在且需要不监视 { dictMonitorObjExpInterrupt.Remove(key, out _); } } else { if (isMonitor) // 对象不存在且需要监视,添加在集合中。 { dictMonitorObjExpInterrupt.TryAdd(key, new List()); ; } } } /// /// 检查一个对象是否处于监听状态,如果是,则传出与该对象相关的表达式(用于中断),如果不是,则返回false。 /// /// /// /// public Task<(bool, string[])> CheckObjMonitorStateAsync(string key) { if (string.IsNullOrEmpty(key)) { var data = (false, Array.Empty()); return Task.FromResult(data); } else { var isMonitor = dictMonitorObjExpInterrupt.TryGetValue(key, out var exps); if (exps is null) { var data = (isMonitor, Array.Empty()); return Task.FromResult(data); } else { var data = (isMonitor, exps.ToArray()); return Task.FromResult(data); } } //if (exps is null) //{ // var data = (isMonitor, Array.Empty()); // return Task.FromResult(data); //} //else //{ // var data = (isMonitor, exps.ToArray()); // return Task.FromResult(data); //} } /// /// 启动器调用,运行到某个节点时触发了监视对象的更新(对象预览视图将会自动更新) /// /// /// /// public void MonitorObjectNotification(string nodeGuid, object monitorData, MonitorObjectEventArgs.ObjSourceType sourceType) { OnMonitorObjectChange?.Invoke(new MonitorObjectEventArgs(nodeGuid, monitorData, sourceType)); } /// /// 启动器调用,节点触发了中断。 /// /// 节点 /// 表达式 /// 类型,0用户主动的中断,1表达式中断 public void TriggerInterrupt(string nodeGuid, string expression, InterruptTriggerEventArgs.InterruptTriggerType type) { OnInterruptTrigger?.Invoke(new InterruptTriggerEventArgs(nodeGuid, expression, type)); } /// /// 环境执行中断 /// /// public async Task GetOrCreateGlobalInterruptAsync() { IsGlobalInterrupt = true; var result = await ChannelFlowInterrupt.GetOrCreateChannelAsync(EnvName); return result; } /// /// 记录节点更改数据,防止重复更改 /// public HashSet<(string, string, object)> NodeValueChangeLogger = new HashSet<(string, string, object)>(); /// /// 数据更改通知(来自远程) /// /// 发生在哪个节点 /// 属性路径 /// 变化后的属性值 /// public async Task NotificationNodeValueChangeAsync(string nodeGuid, string path, object value) { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return; SerinExpressionEvaluator.Evaluate($"@Set .{path} = {value}", nodeModel, out _); // 更改对应的数据 //if (NodeValueChangeLogger.Remove((nodeGuid, path, value))) //{ // // 说明存在过重复的修改 // return; //} //NodeValueChangeLogger.Add((nodeGuid, path, value)); //lock (NodeValueChangeLogger) //{ // Interlocked.Add(ref i, 1); // Console.WriteLine(i); // var getExp = $"@Get .{path}"; // var setExp = $"@Set .{path} = {value}"; // 生成 set 表达式 // var oldValue = SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _); // if(oldValue != value) // { // Console.WriteLine($"旧值:{getExp},result : {oldValue}"); // SerinExpressionEvaluator.Evaluate(setExp, nodeModel, out _); // 更改对应的数据 // Console.WriteLine($"新值:{getExp},result : {SerinExpressionEvaluator.Evaluate(getExp, nodeModel, out _)}"); // } //} } /// /// 改变可选参数的数目 /// /// 对应的节点Guid /// true,增加参数;false,减少参数 /// 以哪个参数为模板进行拷贝,或删去某个参数(该参数必须为可选参数) /// public async Task ChangeParameter(string nodeGuid, bool isAdd, int paramIndex) { var nodeModel = GuidToModel(nodeGuid); if (nodeModel is null) return false; bool isPass; if (isAdd) { isPass = nodeModel.MethodDetails.AddParamsArg(paramIndex); } else { isPass = nodeModel.MethodDetails.RemoveParamsArg(paramIndex); } return isPass; } /// /// Guid 转 NodeModel /// /// 节点Guid /// 节点Model /// 无法获取节点、Guid/节点为null时报错 private NodeModelBase? GuidToModel(string nodeGuid) { if (string.IsNullOrEmpty(nodeGuid)) { //throw new ArgumentNullException("not contains - Guid没有对应节点:" + (nodeGuid)); return null; } if (!NodeModels.TryGetValue(nodeGuid, out NodeModelBase? nodeModel) || nodeModel is null) { //throw new ArgumentNullException("null - Guid存在对应节点,但节点为null:" + (nodeGuid)); return null; } return nodeModel; } #endregion #region 流程依赖类库的接口 /// /// 运行时加载 /// /// 文件名 /// public bool LoadNativeLibraryOfRuning(string file) { return NativeDllHelper.LoadDll(file); } /// /// 运行时加载指定目录下的类库 /// /// 目录 /// 是否递归加载 public void LoadAllNativeLibraryOfRuning(string path, bool isRecurrence = true) { NativeDllHelper.LoadAllDll(path, isRecurrence); } #endregion #region 私有方法 /// /// 加载指定路径的DLL文件 /// /// private void LoadDllNodeInfo(string dllPath) { (var nodeLibrary, var registerTypes, var mdlist) = LoadAssembly(dllPath); if (nodeLibrary is not null && mdlist.Count > 0) { Librarys.TryAdd(nodeLibrary.FullName, nodeLibrary); MethodDetailsOfLibrarys.TryAdd(nodeLibrary, mdlist); foreach (var md in mdlist) { MethodDetailss.TryAdd(md.MethodName, md); } foreach (var kv in registerTypes) { if (!AutoRegisterTypes.TryGetValue(kv.Key, out var types)) { types = new List(); AutoRegisterTypes.Add(kv.Key, types); } types.AddRange(kv.Value); } var mdInfos = mdlist.Select(md => md.ToInfo()).ToList(); // 转换成方法信息 if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnDllLoad?.Invoke(new LoadDllEventArgs(nodeLibrary, mdInfos))); // 通知UI创建dll面板显示 } } } /// /// 移除连接关系 /// /// 起始节点Model /// 目标节点Model /// 连接关系 /// private async Task RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType connectionType) { fromNode.SuccessorNodes[connectionType].Remove(toNode); toNode.PreviousNodes[connectionType].Remove(fromNode); if (OperatingSystem.IsWindows()) { await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( fromNode.Guid, toNode.Guid, JunctionOfConnectionType.Invoke, connectionType, NodeConnectChangeEventArgs.ConnectChangeType.Remote))); } return true; } /// /// 移除连接关系 /// /// 起始节点Model /// 目标节点Model /// 连接关系 /// private async Task RemoteConnectAsync(NodeModelBase fromNode, NodeModelBase toNode, int argIndex) { toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = null; toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = ConnectionArgSourceType.GetPreviousNodeData; // 恢复默认值 if (OperatingSystem.IsWindows()) { await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke(new NodeConnectChangeEventArgs( fromNode.Guid, toNode.Guid, JunctionOfConnectionType.Arg, argIndex, ConnectionArgSourceType.GetPreviousNodeData, NodeConnectChangeEventArgs.ConnectChangeType.Remote))); } return true; } /// /// /// /// /// private (NodeLibrary?, Dictionary>, List) LoadAssembly(string dllPath) { try { //FlowLibraryLoader flowLibraryLoader = new FlowLibraryLoader(dllPath); //Assembly assembly = flowLibraryLoader.LoadFromAssemblyPath(dllPath); Assembly assembly = Assembly.LoadFrom(dllPath); // 加载DLL文件 List types = assembly.GetTypes().ToList(); // 获取程序集中的所有类型 Dictionary> autoRegisterTypes = new Dictionary>(); foreach (Type type in types) { var autoRegisterAttribute = type.GetCustomAttribute(); if (autoRegisterAttribute is not null) { if (!autoRegisterTypes.TryGetValue(autoRegisterAttribute.Class, out var valus)) { valus = new List(); autoRegisterTypes.Add(autoRegisterAttribute.Class, valus); } valus.Add(type); } } List<(Type, string)> scanTypes = types.Select(t => { if (t.GetCustomAttribute() is DynamicFlowAttribute dynamicFlowAttribute && dynamicFlowAttribute.Scan == true) { return (t, dynamicFlowAttribute.Name); } else { return (null, null); } }).Where(it => it.t is not null).ToList(); if (scanTypes.Count == 0) { return (null, [], []); } List methodDetails = new List(); // 遍历扫描的类型 foreach ((var type, var flowName) in scanTypes) { // 加载DLL,创建 MethodDetails、实例作用对象、委托方法 var assemblyName = type.Assembly.GetName().Name; if (string.IsNullOrEmpty(assemblyName)) { continue; } var methods = NodeMethodDetailsHelper.GetMethodsToProcess(type); foreach (var method in methods) { (var md, var del) = NodeMethodDetailsHelper.CreateMethodDetails(type, method, assemblyName); if (md is null || del is null) { Console.WriteLine($"无法加载方法信息:{assemblyName}-{type}-{method}"); continue; } md.MethodAnotherName = flowName + md.MethodAnotherName; if (MethodDelegates.TryAdd(md.MethodName, del)) { methodDetails.Add(md); } else { Console.WriteLine($"节点委托创建失败:{md.MethodName}"); } } } var nodeLibrary = new NodeLibrary { FullName = assembly.GetName().FullName, Assembly = assembly, FileName = Path.GetFileName(dllPath), FilePath = dllPath, }; //LoadedAssemblies.Add(assembly); // 将加载的程序集添加到列表中 //LoadedAssemblyPaths.Add(dllPath); // 记录加载的DLL路径 return (nodeLibrary, autoRegisterTypes, methodDetails); } catch (Exception ex) { Console.WriteLine(ex.ToString()); return (null, [], []); } } /// /// 创建节点 /// /// private bool TryAddNode(NodeModelBase nodeModel) { nodeModel.Guid ??= Guid.NewGuid().ToString(); NodeModels[nodeModel.Guid] = nodeModel; // 如果是触发器,则需要添加到专属集合中 if (nodeModel is SingleFlipflopNode flipflopNode) { var guid = flipflopNode.Guid; if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid))) { FlipflopNodes.Add(flipflopNode); } } return true; } /// /// 检查连接 /// /// 发起连接的起始节点 /// 要连接的目标节点 /// 发起连接节点的控制点类型 /// 被连接节点的控制点类型 /// public static (JunctionOfConnectionType,bool) CheckConnect(NodeModelBase fromNode, NodeModelBase toNode, JunctionType fromNodeJunctionType, JunctionType toNodeJunctionType) { var type = JunctionOfConnectionType.None; var state = false; if (fromNodeJunctionType == JunctionType.Execute) { if (toNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) { // “方法执行”控制点拖拽到“下一节点”控制点,且不是同一个节点, 添加方法执行关系 type = JunctionOfConnectionType.Invoke; state = true; } //else if (toNodeJunctionType == JunctionType.ArgData && fromNode.Guid.Equals(toNode.Guid)) //{ // // “方法执行”控制点拖拽到“方法入参”控制点,且是同一个节点,则添加获取参数关系,表示生成入参参数时自动从该节点的上一节点获取flowdata // type = JunctionOfConnectionType.Arg; // state = true; //} } else if (fromNodeJunctionType == JunctionType.NextStep && !fromNode.Guid.Equals(toNode.Guid)) { // “下一节点”控制点只能拖拽到“方法执行”控制点,且不能是同一个节点 if (toNodeJunctionType == JunctionType.Execute && !fromNode.Guid.Equals(toNode.Guid)) { type = JunctionOfConnectionType.Invoke; state = true; } } else if (fromNodeJunctionType == JunctionType.ArgData) { //if (toNodeJunctionType == JunctionType.Execute && fromNode.Guid.Equals(toNode.Guid)) // 添加获取参数关系 //{ // // “方法入参”控制点拖拽到“方法执行”控制点,且是同一个节点,则添加获取参数关系,生成入参参数时自动从该节点的上一节点获取flowdata // type = JunctionOfConnectionType.Arg; // state = true; //} if(toNodeJunctionType == JunctionType.ReturnData && !fromNode.Guid.Equals(toNode.Guid)) { // “”控制点拖拽到“方法返回值”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata type = JunctionOfConnectionType.Arg; state = true; } } else if (fromNodeJunctionType == JunctionType.ReturnData) { if (toNodeJunctionType == JunctionType.ArgData && !fromNode.Guid.Equals(toNode.Guid)) { // “方法返回值”控制点拖拽到“方法入参”控制点,且不是同一个节点,添加获取参数关系,生成参数时从目标节点获取flowdata type = JunctionOfConnectionType.Arg; state = true; } } // 剩下的情况都是不符预期的连接行为,忽略。 return (type,state); } /// /// 连接节点 /// /// 起始节点 /// 目标节点 /// 连接关系 private bool ConnectInvokeOfNode(NodeModelBase fromNode, NodeModelBase toNode, ConnectionInvokeType invokeType) { if (fromNode is null || toNode is null || fromNode == toNode) { return false; } var ToExistOnFrom = true; var FromExistInTo = true; ConnectionInvokeType[] ct = [ConnectionInvokeType.IsSucceed, ConnectionInvokeType.IsFail, ConnectionInvokeType.IsError, ConnectionInvokeType.Upstream]; if (toNode is SingleFlipflopNode flipflopNode) { flowStarter?.TerminateGlobalFlipflopRuning(flipflopNode); // 假设被连接的是全局触发器,尝试移除 } var isPass = false; foreach (ConnectionInvokeType ctType in ct) { var FToTo = fromNode.SuccessorNodes[ctType].Where(it => it.Guid.Equals(toNode.Guid)).ToArray(); var ToOnF = toNode.PreviousNodes[ctType].Where(it => it.Guid.Equals(fromNode.Guid)).ToArray(); ToExistOnFrom = FToTo.Length > 0; FromExistInTo = ToOnF.Length > 0; if (ToExistOnFrom && FromExistInTo) { Console.WriteLine("起始节点已与目标节点存在连接"); isPass = false; } else { // 检查是否可能存在异常 if (!ToExistOnFrom && FromExistInTo) { Console.WriteLine("目标节点不是起始节点的子节点,起始节点却是目标节点的父节点"); isPass = false; } else if (ToExistOnFrom && !FromExistInTo) { // Console.WriteLine(" 起始节点不是目标节点的父节点,目标节点却是起始节点的子节点"); isPass = false; } else { isPass = true; } } } if (isPass) { fromNode.SuccessorNodes[invokeType].Add(toNode); // 添加到起始节点的子分支 toNode.PreviousNodes[invokeType].Add(fromNode); // 添加到目标节点的父分支 if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( fromNode.Guid, // 从哪个节点开始 toNode.Guid, // 连接到那个节点 JunctionOfConnectionType.Invoke, invokeType, // 连接线的样式类型 NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 ))); // 通知UI } // Invoke // GetResult return true; } else { return false; } } /// /// 连接节点参数 /// /// /// /// /// /// private async Task ConnectArgSourceOfNodeAsync(NodeModelBase fromNode, NodeModelBase toNode, ConnectionArgSourceType connectionArgSourceType, int argIndex) { var oldNodeGuid = toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid; if (!string.IsNullOrEmpty(oldNodeGuid)) { //if(NodeModels.TryGetValue(oldNodeGuid, out var oldNodeModel)) //{ // // 已经存在连接 // await RemoteConnectAsync(oldNodeModel, toNode, argIndex); // 已经存在连接,将其移除 //} if (oldNodeGuid.Equals(fromNode.Guid)) { await RemoteConnectAsync(fromNode, toNode, argIndex); // 相同起始节点不同控制点已经连接,将其移除 } else { Console.WriteLine("目标入参已确定参数来源,不可连接 "); return false; } } toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceNodeGuid = fromNode.Guid; toNode.MethodDetails.ParameterDetailss[argIndex].ArgDataSourceType = connectionArgSourceType; await UIContextOperation.InvokeAsync(() => OnNodeConnectChange?.Invoke( new NodeConnectChangeEventArgs( fromNode.Guid, // 从哪个节点开始 toNode.Guid, // 连接到那个节点 JunctionOfConnectionType.Arg, (int)argIndex, // 连接线的样式类型 connectionArgSourceType, NodeConnectChangeEventArgs.ConnectChangeType.Create // 是创建连接还是删除连接 ))); // 通知UI return true; } /// /// 更改起点节点 /// /// /// private void SetStartNode(NodeModelBase newStartNode) { var oldNodeGuid = StartNode?.Guid; StartNode = newStartNode; if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnStartNodeChange?.Invoke(new StartNodeChangeEventArgs(oldNodeGuid, StartNode.Guid))); } } /// /// 输出内容 /// /// private void Output(string msg) { if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnEnvOut?.Invoke(msg)); } } #endregion #region 视觉效果 /// /// 定位节点 /// /// public void NodeLocated(string nodeGuid) { if (OperatingSystem.IsWindows()) { UIContextOperation?.Invoke(() => OnNodeLocated?.Invoke(new NodeLocatedEventArgs(nodeGuid))); } } #endregion #region IOC容器相关 ISereinIOC ISereinIOC.Reset() { sereinIOC.Reset(); return this; } ISereinIOC ISereinIOC.Register(Type type, params object[] parameters) { sereinIOC.Register(type, parameters); return this; } ISereinIOC ISereinIOC.Register(params object[] parameters) { sereinIOC.Register(parameters); return this; } ISereinIOC ISereinIOC.Register(params object[] parameters) { sereinIOC.Register(parameters); return this; } //T ISereinIOC.GetOrRegisterInstantiate() //{ // return sereinIOC.GetOrRegisterInstantiate(); //} //object ISereinIOC.GetOrRegisterInstantiate(Type type) //{ // return sereinIOC.GetOrRegisterInstantiate(type); //} object ISereinIOC.Get(Type type) { return sereinIOC.Get(type); } T ISereinIOC.Get() { return (T)sereinIOC.Get(typeof(T)); } T ISereinIOC.Get(string key) { return sereinIOC.Get(key); } bool ISereinIOC.CustomRegisterInstance(string key, object instance, bool needInjectProperty) { return sereinIOC.CustomRegisterInstance(key, instance, needInjectProperty); } object ISereinIOC.Instantiate(Type type) { return sereinIOC.Instantiate(type); } T ISereinIOC.Instantiate() { return sereinIOC.Instantiate(); } ISereinIOC ISereinIOC.Build() { sereinIOC.Build(); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } ISereinIOC ISereinIOC.Run(Action action) { sereinIOC.Run(action); return this; } #endregion } }