From 75333e621fcd77f43faf9d5a23703ffdafdf0f7b Mon Sep 17 00:00:00 2001
From: fengjiayi <12821976+ning_xi@user.noreply.gitee.com>
Date: Mon, 5 Aug 2024 10:11:58 +0800
Subject: [PATCH] =?UTF-8?q?GIT=E7=BB=83=E4=B9=A0?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
.gitattributes | 63 +
LICENSE | 21 +
Library/DbSql/DBSync.cs | 576 +++++
Library/DbSql/FifoManualResetEvent.cs | 71 +
Library/DbSql/IRepositoryBase.cs | 19 +
Library/DbSql/RepositoryBase.cs | 855 +++++++
Library/DynamicFlow/Api.cs | 13 +
Library/DynamicFlow/Attribute.cs | 73 +
Library/DynamicFlow/DynamicContext.cs | 165 ++
Library/DynamicFlow/MethodDetails.cs | 218 ++
Library/DynamicFlow/NodeFlowStarter.cs | 158 ++
.../NodeModel/CompositeActionNode.cs | 54 +
.../NodeModel/CompositeConditionNode.cs | 69 +
.../NodeModel/CompositeLoopNode.cs | 12 +
Library/DynamicFlow/NodeModel/NodeBase.cs | 449 ++++
.../DynamicFlow/NodeModel/SingleActionNode.cs | 71 +
.../NodeModel/SingleConditionNode.cs | 72 +
.../DynamicFlow/NodeModel/SingleExpOpNode.cs | 44 +
.../NodeModel/SingleFlipflopNode.cs | 40 +
.../SerinExpression/ConditionResolver.cs | 320 +++
.../SerinExpression/SerinConditionParser.cs | 318 +++
.../SerinExpressionEvaluator.cs | 196 ++
Library/DynamicFlow/Tool/DelegateGenerator.cs | 186 ++
Library/DynamicFlow/Tool/DynamicTool.cs | 203 ++
Library/DynamicFlow/Tool/ExpressionHelper.cs | 740 ++++++
Library/DynamicFlow/Tool/TcsSignal.cs | 94 +
Library/DynamicFlow/Tool/TypeDefinition.cs | 43 +
Library/Serein.Library.csproj | 15 +
Library/ServiceContainer.cs | 376 +++
Library/Tool/DataHelper.cs | 169 ++
Library/Web/Attribute.cs | 107 +
Library/Web/ControllerBase.cs | 15 +
Library/Web/Router.cs | 692 ++++++
Library/Web/WebAPIAttribute.cs | 184 ++
MyDll/MyDll.csproj | 22 +
MyDll/SampleCondition.cs | 247 ++
README.md | 3 +
SereinFlow.sln | 37 +
WorkBench/App.xaml | 16 +
WorkBench/App.xaml.cs | 170 ++
WorkBench/AssemblyInfo.cs | 10 +
WorkBench/LogWindow.xaml | 22 +
WorkBench/LogWindow.xaml.cs | 33 +
WorkBench/MainWindow.xaml | 92 +
WorkBench/MainWindow.xaml.cs | 2031 +++++++++++++++++
WorkBench/Node/NodeBase.cs | 270 +++
WorkBench/Node/View/ActionNodeControl.xaml | 41 +
WorkBench/Node/View/ActionNodeControl.xaml.cs | 22 +
WorkBench/Node/View/ActionRegionControl.xaml | 30 +
.../Node/View/ActionRegionControl.xaml.cs | 147 ++
WorkBench/Node/View/ConditionNodeControl.xaml | 75 +
.../Node/View/ConditionNodeControl.xaml.cs | 37 +
.../Node/View/ConditionRegionControl.xaml | 34 +
.../Node/View/ConditionRegionControl.xaml.cs | 125 +
WorkBench/Node/View/DllControlControl.xaml | 41 +
WorkBench/Node/View/DllControlControl.xaml.cs | 146 ++
WorkBench/Node/View/ExpOpNodeControl.xaml | 20 +
WorkBench/Node/View/ExpOpNodeControl.xaml.cs | 43 +
WorkBench/Node/View/FlipflopNodeControl.xaml | 34 +
.../Node/View/FlipflopNodeControl.xaml.cs | 41 +
WorkBench/Node/View/NodeControlBase.cs | 77 +
.../ViewModel/ActionNodeControlViewModel.cs | 33 +
.../ConditionNodeControlViewModel.cs | 49 +
.../Node/ViewModel/ExpOpNodeViewModel.cs | 31 +
.../ViewModel/FlipflopNodeControlViewModel.cs | 15 +
.../Node/ViewModel/TypeToStringConverter.cs | 27 +
WorkBench/Serein.WorkBench.csproj | 52 +
WorkBench/SereinOutputFileData.cs | 133 ++
.../Condition/BoolConditionControl.xaml | 16 +
.../Condition/BoolConditionControl.xaml.cs | 28 +
.../Themes/Condition/IntConditionControl.xaml | 21 +
.../Condition/IntConditionControl.xaml.cs | 28 +
WorkBench/Themes/Condition/Model.cs | 88 +
.../Condition/StringConditionControl.xaml | 18 +
.../Condition/StringConditionControl.xaml.cs | 28 +
WorkBench/Themes/ConditionControl.xaml | 35 +
WorkBench/Themes/ConditionControl.xaml.cs | 85 +
WorkBench/Themes/ConditionControlModel.cs | 99 +
WorkBench/Themes/MethodDetailsControl.xaml | 115 +
WorkBench/Themes/MethodDetailsControl.xaml.cs | 74 +
WorkBench/Themes/MultiConditionConverter.xaml | 4 +
WorkBench/Themes/TypeViewerWindow.xaml | 15 +
WorkBench/Themes/TypeViewerWindow.xaml.cs | 71 +
WorkBench/tool/LogTextWriter.cs | 45 +
84 files changed, 11677 insertions(+)
create mode 100644 .gitattributes
create mode 100644 LICENSE
create mode 100644 Library/DbSql/DBSync.cs
create mode 100644 Library/DbSql/FifoManualResetEvent.cs
create mode 100644 Library/DbSql/IRepositoryBase.cs
create mode 100644 Library/DbSql/RepositoryBase.cs
create mode 100644 Library/DynamicFlow/Api.cs
create mode 100644 Library/DynamicFlow/Attribute.cs
create mode 100644 Library/DynamicFlow/DynamicContext.cs
create mode 100644 Library/DynamicFlow/MethodDetails.cs
create mode 100644 Library/DynamicFlow/NodeFlowStarter.cs
create mode 100644 Library/DynamicFlow/NodeModel/CompositeActionNode.cs
create mode 100644 Library/DynamicFlow/NodeModel/CompositeConditionNode.cs
create mode 100644 Library/DynamicFlow/NodeModel/CompositeLoopNode.cs
create mode 100644 Library/DynamicFlow/NodeModel/NodeBase.cs
create mode 100644 Library/DynamicFlow/NodeModel/SingleActionNode.cs
create mode 100644 Library/DynamicFlow/NodeModel/SingleConditionNode.cs
create mode 100644 Library/DynamicFlow/NodeModel/SingleExpOpNode.cs
create mode 100644 Library/DynamicFlow/NodeModel/SingleFlipflopNode.cs
create mode 100644 Library/DynamicFlow/SerinExpression/ConditionResolver.cs
create mode 100644 Library/DynamicFlow/SerinExpression/SerinConditionParser.cs
create mode 100644 Library/DynamicFlow/SerinExpression/SerinExpressionEvaluator.cs
create mode 100644 Library/DynamicFlow/Tool/DelegateGenerator.cs
create mode 100644 Library/DynamicFlow/Tool/DynamicTool.cs
create mode 100644 Library/DynamicFlow/Tool/ExpressionHelper.cs
create mode 100644 Library/DynamicFlow/Tool/TcsSignal.cs
create mode 100644 Library/DynamicFlow/Tool/TypeDefinition.cs
create mode 100644 Library/Serein.Library.csproj
create mode 100644 Library/ServiceContainer.cs
create mode 100644 Library/Tool/DataHelper.cs
create mode 100644 Library/Web/Attribute.cs
create mode 100644 Library/Web/ControllerBase.cs
create mode 100644 Library/Web/Router.cs
create mode 100644 Library/Web/WebAPIAttribute.cs
create mode 100644 MyDll/MyDll.csproj
create mode 100644 MyDll/SampleCondition.cs
create mode 100644 README.md
create mode 100644 SereinFlow.sln
create mode 100644 WorkBench/App.xaml
create mode 100644 WorkBench/App.xaml.cs
create mode 100644 WorkBench/AssemblyInfo.cs
create mode 100644 WorkBench/LogWindow.xaml
create mode 100644 WorkBench/LogWindow.xaml.cs
create mode 100644 WorkBench/MainWindow.xaml
create mode 100644 WorkBench/MainWindow.xaml.cs
create mode 100644 WorkBench/Node/NodeBase.cs
create mode 100644 WorkBench/Node/View/ActionNodeControl.xaml
create mode 100644 WorkBench/Node/View/ActionNodeControl.xaml.cs
create mode 100644 WorkBench/Node/View/ActionRegionControl.xaml
create mode 100644 WorkBench/Node/View/ActionRegionControl.xaml.cs
create mode 100644 WorkBench/Node/View/ConditionNodeControl.xaml
create mode 100644 WorkBench/Node/View/ConditionNodeControl.xaml.cs
create mode 100644 WorkBench/Node/View/ConditionRegionControl.xaml
create mode 100644 WorkBench/Node/View/ConditionRegionControl.xaml.cs
create mode 100644 WorkBench/Node/View/DllControlControl.xaml
create mode 100644 WorkBench/Node/View/DllControlControl.xaml.cs
create mode 100644 WorkBench/Node/View/ExpOpNodeControl.xaml
create mode 100644 WorkBench/Node/View/ExpOpNodeControl.xaml.cs
create mode 100644 WorkBench/Node/View/FlipflopNodeControl.xaml
create mode 100644 WorkBench/Node/View/FlipflopNodeControl.xaml.cs
create mode 100644 WorkBench/Node/View/NodeControlBase.cs
create mode 100644 WorkBench/Node/ViewModel/ActionNodeControlViewModel.cs
create mode 100644 WorkBench/Node/ViewModel/ConditionNodeControlViewModel.cs
create mode 100644 WorkBench/Node/ViewModel/ExpOpNodeViewModel.cs
create mode 100644 WorkBench/Node/ViewModel/FlipflopNodeControlViewModel.cs
create mode 100644 WorkBench/Node/ViewModel/TypeToStringConverter.cs
create mode 100644 WorkBench/Serein.WorkBench.csproj
create mode 100644 WorkBench/SereinOutputFileData.cs
create mode 100644 WorkBench/Themes/Condition/BoolConditionControl.xaml
create mode 100644 WorkBench/Themes/Condition/BoolConditionControl.xaml.cs
create mode 100644 WorkBench/Themes/Condition/IntConditionControl.xaml
create mode 100644 WorkBench/Themes/Condition/IntConditionControl.xaml.cs
create mode 100644 WorkBench/Themes/Condition/Model.cs
create mode 100644 WorkBench/Themes/Condition/StringConditionControl.xaml
create mode 100644 WorkBench/Themes/Condition/StringConditionControl.xaml.cs
create mode 100644 WorkBench/Themes/ConditionControl.xaml
create mode 100644 WorkBench/Themes/ConditionControl.xaml.cs
create mode 100644 WorkBench/Themes/ConditionControlModel.cs
create mode 100644 WorkBench/Themes/MethodDetailsControl.xaml
create mode 100644 WorkBench/Themes/MethodDetailsControl.xaml.cs
create mode 100644 WorkBench/Themes/MultiConditionConverter.xaml
create mode 100644 WorkBench/Themes/TypeViewerWindow.xaml
create mode 100644 WorkBench/Themes/TypeViewerWindow.xaml.cs
create mode 100644 WorkBench/tool/LogTextWriter.cs
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..1ff0c42
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,63 @@
+###############################################################################
+# Set default behavior to automatically normalize line endings.
+###############################################################################
+* text=auto
+
+###############################################################################
+# Set default behavior for command prompt diff.
+#
+# This is need for earlier builds of msysgit that does not have it on by
+# default for csharp files.
+# Note: This is only used by command line
+###############################################################################
+#*.cs diff=csharp
+
+###############################################################################
+# Set the merge driver for project and solution files
+#
+# Merging from the command prompt will add diff markers to the files if there
+# are conflicts (Merging from VS is not affected by the settings below, in VS
+# the diff markers are never inserted). Diff markers may cause the following
+# file extensions to fail to load in VS. An alternative would be to treat
+# these files as binary and thus will always conflict and require user
+# intervention with every merge. To do so, just uncomment the entries below
+###############################################################################
+#*.sln merge=binary
+#*.csproj merge=binary
+#*.vbproj merge=binary
+#*.vcxproj merge=binary
+#*.vcproj merge=binary
+#*.dbproj merge=binary
+#*.fsproj merge=binary
+#*.lsproj merge=binary
+#*.wixproj merge=binary
+#*.modelproj merge=binary
+#*.sqlproj merge=binary
+#*.wwaproj merge=binary
+
+###############################################################################
+# behavior for image files
+#
+# image files are treated as binary by default.
+###############################################################################
+#*.jpg binary
+#*.png binary
+#*.gif binary
+
+###############################################################################
+# diff behavior for common document formats
+#
+# Convert binary document formats to text before diffing them. This feature
+# is only available from the command line. Turn it on by uncommenting the
+# entries below.
+###############################################################################
+#*.doc diff=astextplain
+#*.DOC diff=astextplain
+#*.docx diff=astextplain
+#*.DOCX diff=astextplain
+#*.dot diff=astextplain
+#*.DOT diff=astextplain
+#*.pdf diff=astextplain
+#*.PDF diff=astextplain
+#*.rtf diff=astextplain
+#*.RTF diff=astextplain
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..14317c3
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 fengjiayi
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/Library/DbSql/DBSync.cs b/Library/DbSql/DBSync.cs
new file mode 100644
index 0000000..5caa4a9
--- /dev/null
+++ b/Library/DbSql/DBSync.cs
@@ -0,0 +1,576 @@
+using SqlSugar;
+using System.ComponentModel;
+using System.Net.Sockets;
+using System.Reflection;
+
+namespace Serein.DbSql
+{
+ public enum DBSyncStart
+ {
+ ///
+ /// 无需同步
+ ///
+ [Description("无需同步")]
+ NotNeed,
+ ///
+ /// 同步成功
+ ///
+ [Description("同步成功")]
+ SyncSuccess,
+ ///
+ /// 同步失败
+ ///
+ [Description("同步失败")]
+ SyncFailure,
+ ///
+ /// 连接异常
+ ///
+ [Description("配置/连接异常")]
+ NetworkError,
+ ///
+ /// 没有同步事件
+ ///
+ [Description("没有同步事件,请使用 DBSync.SetSyncDataEvent() 方法设置同步事件")]
+ NoEvent,
+ }
+ public enum DBSyncExType
+ {
+ [Description("连接异常")]
+ ConnectError,
+ [Description("读写异常")]
+ CrudError,
+ [Description("同步异常")]
+ SyncError,
+ }
+
+ public class DBSyncConfig
+ {
+ public DBSyncConfig(ConnectionConfig primaryDBConfig,
+ ConnectionConfig secondaryDBConfig)
+ {
+ PrimaryDBConfig = primaryDBConfig;
+ SecondaryDBConfig = secondaryDBConfig;
+ }
+ ///
+ /// 主数据库IP
+ ///
+ //private string Host { get; }
+ ///
+ /// 主数据库端口
+ ///
+ //private int Port { get; }
+ ///
+ /// 主数据库配置
+ ///
+ private ConnectionConfig PrimaryDBConfig { get; }
+ ///
+ /// 从数据库配置
+ ///
+ private ConnectionConfig SecondaryDBConfig { get; }
+
+ public override string ToString()
+ {
+ return $"[主数据库配置]{PrimaryDBConfig.ConnectionString}" + Environment.NewLine +
+ $"[从数据库配置]{SecondaryDBConfig.ConnectionString}" + Environment.NewLine;
+ }
+
+ ///
+ /// 检查网络状态
+ ///
+ ///
+ public bool GetNetworkState()
+ {
+ var isOpen = DBSync.IsPortOpen(); // 数据库基类获取网络状态
+ if (!isOpen)
+ {
+ DBSync.SetIsNeedSyncData(true); // 远程数据库查询失败,尝试本地数据库
+ }
+ return isOpen;
+ }
+
+ ///
+ /// 返回从数据库
+ ///
+ ///
+ public SqlSugarClient GetSecondaryDB()
+ {
+ DBSync.SyncEvent.Wait();
+ return new SqlSugarClient(SecondaryDBConfig);
+ }
+
+ ///
+ /// 返回主数据库
+ ///
+ ///
+ ///
+ public SqlSugarClient GetPrimaryDB()
+ {
+ try
+ {
+ // 等待同步事件
+ DBSync.SyncEvent.Wait();
+ // 检查主数据库连接状态
+ if (!DBSync.IsPortOpen()) // 返回主数据库检测网络状态
+ {
+ // Console.WriteLine($"主数据库无法连接,IP:{IP},端口:{Port}");
+ DBSync.SetIsNeedSyncData(true); // 网络不可达
+ return null;
+ }
+
+ // 检查是否需要同步数据
+ /*if (DBSync.GetIsNeedSyncData())
+ {
+ var syncState = DBSync.StartSyncDataBase();
+ if (syncState != DBSyncStart.SyncSuccess && syncState != DBSyncStart.NotNeed)
+ {
+ // Console.WriteLine($"获取读写客户端前,尝试同步时发生异常:{DBSync.GetDescription(syncState)}");
+ return null;
+ }
+ }*/
+
+ // 返回主数据库客户端
+ return new SqlSugarClient(PrimaryDBConfig);
+ }
+ catch // (Exception ex)
+ {
+ // Console.WriteLine($"发生异常:{ex.Message}");
+ return null;
+ }
+ }
+
+
+ }
+
+ ///
+ /// 数据库同步异常
+ ///
+ public class DBSyncException : Exception
+ {
+ public DBSyncExType ExceptionType { get; private set; }
+
+ public DBSyncException(DBSyncExType exceptionType)
+ {
+ ExceptionType = exceptionType;
+ }
+
+ public DBSyncException(DBSyncExType exceptionType, string message) : base(message)
+ {
+ ExceptionType = exceptionType;
+ }
+
+ public DBSyncException(DBSyncExType exceptionType, string message, Exception innerException) : base(message, innerException)
+ {
+ ExceptionType = exceptionType;
+ }
+ public override string ToString()
+ {
+ return $"异常: {ExceptionType}: {GetDescription(ExceptionType)}. Message: {Message}";
+ }
+ public static string GetDescription(DBSyncExType value)
+ {
+ FieldInfo field = value.GetType().GetField(value.ToString());
+ DescriptionAttribute attribute = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute));
+ return attribute == null ? value.ToString() : attribute.Description;
+ }
+
+ }
+
+
+ ///
+ /// 远程、本地数据库同步
+ ///
+ public static class DBSync
+ {
+ ///
+ /// 主数据库配置
+ ///
+ private static ConnectionConfig PrimaryConfig { get; set; }
+ ///
+ /// 从数据库配置
+ ///
+ private static ConnectionConfig SecondaryConfig { get; set; }
+ ///
+ /// 主数据库IP
+ ///
+ private static string Host { get; set; }
+ ///
+ /// 主数据库端口
+ ///
+ private static int Port { get; set; }
+ ///
+ /// 同步数据事件(远程数据库,本地数据库,是否执行成功)
+ ///
+ private static Func SyncDataEvent { get; set; }
+ private static Action StateChangeEvent { get; set; }
+ ///
+ /// 数据库设置锁
+ ///
+ //private static object DBSetLock { get; set; } = new object();
+ ///
+ /// 是否需要同步数据
+ ///
+ private static bool IsNeedSyncData { get; set; } = false;
+ ///
+ /// 等待次数(执行了多少次操作后才尝试进行同步,设置为0容易影响性能)
+ ///
+ private static int WaitCount { get; set; } = 10;
+
+ ///
+ /// 客户端获取计数
+ ///
+ private static int CrudDBGetCount { get; set; } = 0;
+ ///
+ /// 同步端获取计数
+ ///
+ private static int SyncDBGetCount { get; set; } = 0;
+
+
+ //public static ManualResetEventSlim SyncEvent { get; } = new ManualResetEventSlim(true); // 同步事件
+ ///
+ /// 远程本地同步阻塞事件
+ ///
+ public static FifoManualResetEvent SyncEvent { get; } = new FifoManualResetEvent(true);
+ ///
+ /// 数据同步锁
+ ///
+ private static object SyncLock { get; } = new object();
+ ///
+ /// 是否需要同步数据读写锁
+ ///
+ private static readonly ReaderWriterLockSlim NeedSyncStateLock = new ReaderWriterLockSlim();
+
+ ///
+ /// 是否断开过,true=断开过,false=没有断开过
+ /// 设置为 false 时自动检测网络情况,只有在网络正常的情况下才能成功设置为 true
+ ///
+ ///
+ public static void SetIsNeedSyncData(bool value)
+ {
+ if (value == IsNeedSyncData)
+ {
+ return;
+ }
+ //Console.WriteLine("变更数据库");
+ // 写入锁
+ NeedSyncStateLock.EnterWriteLock();
+ try
+ {
+ if (value)
+ {
+ IsNeedSyncData = true;
+ return;
+ }
+ IsNeedSyncData = !IsPortOpen(); // 变更 是否同步 属性时获取网络状态
+ }
+ finally
+ {
+ NeedSyncStateLock.ExitWriteLock();
+ StateChangeEvent?.Invoke(IsNeedSyncData);
+ }
+ }
+ public static bool GetIsNeedSyncData()
+ {
+ // 读取锁
+ NeedSyncStateLock.EnterReadLock();
+ try
+ {
+ return IsNeedSyncData; //是否需要同步数据
+ }
+ finally
+ {
+ NeedSyncStateLock.ExitReadLock();
+ }
+ }
+
+
+
+ ///
+ /// 配置主数据库
+ ///
+ public static void PrimaryConnect(DbType dbType, string host, int port, string dbName, string user, string password)
+ {
+ Host = host;
+ Port = port;
+ PrimaryConfig = GetConnectionConfig(dbType, host, port.ToString(), dbName, user, password);
+
+ /*SyncEvent.Wait();
+
+ if (true || IsPortOpen(host, port))
+ {
+ // 目标端口打通时才会更改数据库配置
+ lock (DBSetLock)
+ {
+ Host = host;
+ Port = port;
+ PrimaryConfig = GetConnectionConfig(dbType, host, port.ToString(), dbName, user, password);
+ }
+ }
+ else
+ {
+ throw new DBSyncException(DBSyncExType.ConnectError, $"主数据库配置失败,无法连接,目标配置:IP:{host},端口:{port},目标库名:{dbName},账户:{user}");
+ }*/
+ }
+ ///
+ /// 配置从数据库
+ ///
+ public static void SecondaryConnect(DbType dbType, string host, int port, string dbName, string user, string password)
+ {
+ SecondaryConfig = GetConnectionConfig(dbType, host, port.ToString(), dbName, user, password);
+
+ /*if (IsPortOpen(host, port))
+ {
+ lock (DBSetLock)
+ {
+ SecondaryConfig = GetConnectionConfig(dbType, host, port.ToString(), dbName, user, password);
+ }
+ }
+ else
+ {
+ throw new DBSyncException(DBSyncExType.ConnectError, $"从数据库配置失败,无法连接,目标配置:{host},端口:{port},目标库名:{dbName},账户:{user}");
+ }*/
+ }
+
+ ///
+ /// 尝试执行一次数据同步
+ ///
+ public static bool SyncData()
+ {
+ SetIsNeedSyncData(true);
+ var state = StartSyncDataBase(true); // 手动同步
+ return state == DBSyncStart.SyncSuccess || state == DBSyncStart.NotNeed;
+ }
+
+
+
+ ///
+ /// 设置同步事件与等待次数。
+ ///
+ /// 同步事件(需要手动同步数据)
+ /// 等待次数(执行了多少次操作后才尝试进行同步,设置为0容易影响性能)
+ public static void SetSyncEvent(Func syncDataEvent, int waitCount = 0)
+ {
+ SyncDataEvent = syncDataEvent;
+ WaitCount = waitCount;
+ }
+ ///
+ /// 设置状态变化事件
+ ///
+ ///
+ ///
+ public static void SetStateChangeEvent(Action stateChangeEvent)
+ {
+ StateChangeEvent = stateChangeEvent;
+ }
+
+ ///
+ /// 获取数据库配置(不推荐使用在除了Repository的地方外部调用)
+ ///
+ ///
+ public static DBSyncConfig GetSyncSqlConfig()
+ {
+ /*SyncEvent.Wait();
+ */
+
+ if (GetIsNeedSyncData())
+ {
+ _ = Task.Run(() => StartSyncDataBase()); // new了一个RepositoryBase时尝试同步数据
+ }
+
+ lock (SyncLock)
+ {
+ CrudDBGetCount++;
+ //Console.WriteLine($"获取客户端:{CrudDBGetCount}");
+ return new DBSyncConfig(PrimaryConfig, SecondaryConfig);
+ }
+ }
+
+ public static void ReSetCrudDb()
+ {
+ CrudDBGetCount--;
+ Task.Run(() => StartSyncDataBase()); // 释放数据库连接时尝试同步数据
+
+ /*if (GetIsNeedSyncData())
+ {
+
+ }*/
+ // Console.WriteLine($"释放客户端:{CrudDBGetCount}");
+ }
+
+ public static DBSyncStart StartSyncDataBase(bool isAtOnce = false)
+ {
+ /*if (!isAtOnce && WaitCount > 0)
+ {
+ WaitCount--;
+ return DBSyncStart.NotNeed;
+ }*/
+
+ SyncEvent.Reset(); // 锁定线程,保证只有一个线程进入该方法
+
+ if (!GetIsNeedSyncData())
+ {
+ SyncEvent.Set();
+ return DBSyncStart.NotNeed;
+ }
+
+ if (!IsPortOpen()) // 同步时获取网络状态
+ {
+ SetIsNeedSyncData(true);
+ SyncEvent.Set();
+ return DBSyncStart.NetworkError;
+ }
+
+
+
+ if (SyncDataEvent == null)
+ {
+ SyncEvent.Set();
+ return DBSyncStart.NoEvent;
+ }
+
+
+ lock (SyncLock) // 同步锁,避免其它符合进入条件的线程执行多次同步
+ {
+ if (!GetIsNeedSyncData())
+ {
+ SyncEvent.Set();
+ return DBSyncStart.NotNeed;
+ }
+ Console.WriteLine("网络检测OK,准备同步数据");
+ try
+ {
+ bool isSuccess = SyncDataEvent.Invoke(new SqlSugarClient(PrimaryConfig), new SqlSugarClient(SecondaryConfig));
+ SetIsNeedSyncData(!isSuccess);
+
+ if (isSuccess)
+ {
+ return DBSyncStart.SyncSuccess;
+ }
+ else
+ {
+ return DBSyncStart.SyncFailure;
+ }
+ }
+ catch (Exception ex)
+ {
+ // 记录异常日志
+ Console.WriteLine($"同步数据时发生异常: {ex.Message}");
+ return DBSyncStart.SyncFailure;
+ }
+ finally
+ {
+ SyncEvent.Set(); // 释放同步事件,以防止其他线程一直被阻塞
+ }
+ }
+
+ }
+
+
+ public static string GetDescription(DBSyncStart value)
+ {
+ FieldInfo field = value.GetType().GetField(value.ToString());
+ DescriptionAttribute attribute = (DescriptionAttribute)field.GetCustomAttribute(typeof(DescriptionAttribute));
+ return attribute == null ? value.ToString() : attribute.Description;
+ }
+
+ ///
+ /// 检测目标地址是否打通
+ ///
+ /// ip地址
+ /// 端口号
+ /// 超时时间
+ ///
+ public static bool IsPortOpen(string ip, int port, int timeout = 300)
+ {
+ using (var client = new TcpClient())
+ {
+ var result = client.ConnectAsync(ip, port);
+ try
+ {
+ var open = result.Wait(timeout);
+ return open;
+ }
+ catch (SocketException)
+ {
+ return false;
+ }
+ }
+ }
+ ///
+ /// 检测目标地址是否打通:主数据库IP和端口是否打通(true通,false断)
+ ///
+ /// 超时时间
+ ///
+ public static bool IsPortOpen(int timeout = 300)
+ {
+ string ip = Host;
+ int port = Port;
+ using (var client = new TcpClient())
+ {
+ bool isOpen = true;
+ try
+ {
+ var result = client.ConnectAsync(ip, port);
+ isOpen = result.Wait(timeout);
+ if (!isOpen)
+ {
+ //Console.WriteLine($"连接超时{ip},{port}");
+ }
+ return isOpen;
+ }
+ catch
+ {
+ isOpen = false;
+ return isOpen;
+ }
+ finally
+ {
+ //Console.WriteLine("网络检测:" + isOpen);
+ }
+ }
+ }
+
+ ///
+ /// 返回数据库连接串
+ ///
+ /// 数据库类型
+ /// 服务器IP地址
+ /// 数据库名
+ /// 登录账户
+ /// 登录密码
+ private static ConnectionConfig GetConnectionConfig(DbType dbType, string host, string port, string dbName, string name, string password)
+ {
+ ConnectionConfig config;
+ string ConnectionString;
+ switch (dbType)
+ {
+ case DbType.MySql:
+ ConnectionString = $"Server={host};DataBase={dbName};Port={port};UserId={name};Password={password};Persist Security Info=True;Allow Zero Datetime=True;Character Set=utf8;";
+ config = new ConnectionConfig()
+ {
+ ConnectionString = ConnectionString,//连接符字串
+ DbType = DbType.MySql,
+ IsAutoCloseConnection = true,
+ InitKeyType = InitKeyType.Attribute //从实体特性中读取主键自增列信息
+ };
+
+ break;
+ case DbType.SqlServer:
+ ConnectionString = $"Server={host},{port};DataBase={dbName};uid={name};pwd={password}";
+ config = new ConnectionConfig()
+ {
+ ConnectionString = ConnectionString,//连接符字串
+ DbType = DbType.SqlServer,
+ IsAutoCloseConnection = true,
+ InitKeyType = InitKeyType.Attribute //从实体特性中读取主键自增列信息
+ };
+ break;
+ default:
+ config = null;
+ break;
+ }
+ return config;
+
+ }
+ }
+
+}
diff --git a/Library/DbSql/FifoManualResetEvent.cs b/Library/DbSql/FifoManualResetEvent.cs
new file mode 100644
index 0000000..ca9a8f4
--- /dev/null
+++ b/Library/DbSql/FifoManualResetEvent.cs
@@ -0,0 +1,71 @@
+namespace Serein.DbSql
+{
+ ///
+ /// 线程阻塞
+ ///
+ public class FifoManualResetEvent
+ {
+ private readonly object lockObj = new object();
+ ///
+ /// 让线程按进入时间顺序调用
+ ///
+ private readonly Queue waitQueue = new Queue();
+ private bool isSet;
+
+ public bool IsSet { get => isSet; set => isSet = value; }
+
+ public FifoManualResetEvent(bool initialState = false)
+ {
+ IsSet = initialState;
+ }
+
+ ///
+ /// 等待解锁
+ ///
+ public void Wait()
+ {
+ lock (lockObj)
+ {
+ if (IsSet)
+ {
+ // 获取到了发送的信号,线程开始重新执行
+ return;
+ }
+
+ var currentThread = Thread.CurrentThread;
+ waitQueue.Enqueue(currentThread);
+
+ while (!IsSet || waitQueue.Peek() != currentThread)
+ {
+ Monitor.Wait(lockObj);
+ }
+
+ waitQueue.Dequeue();
+ }
+ }
+
+ ///
+ /// 发送信号
+ ///
+ public void Set()
+ {
+ lock (lockObj)
+ {
+ IsSet = true;
+ Monitor.PulseAll(lockObj);
+ }
+ }
+
+ ///
+ /// 锁定当前线程
+ ///
+ public void Reset()
+ {
+ lock (lockObj)
+ {
+ IsSet = false;
+ }
+ }
+ }
+
+}
diff --git a/Library/DbSql/IRepositoryBase.cs b/Library/DbSql/IRepositoryBase.cs
new file mode 100644
index 0000000..1df6707
--- /dev/null
+++ b/Library/DbSql/IRepositoryBase.cs
@@ -0,0 +1,19 @@
+using System.Linq.Expressions;
+
+namespace Serein.DbSql
+{
+ public interface IRepositoryBase where TEntity : class, new()
+ {
+ TEntity GetModelByID(dynamic ID);
+
+ int Add(TEntity Model);
+
+ int Update(TEntity Model);
+
+ bool DeleteByID(dynamic ID);
+
+ bool Delete(Expression> where);
+
+ int UpdateColumns(TEntity model, Expression> expression);
+ }
+}
diff --git a/Library/DbSql/RepositoryBase.cs b/Library/DbSql/RepositoryBase.cs
new file mode 100644
index 0000000..e98ba8f
--- /dev/null
+++ b/Library/DbSql/RepositoryBase.cs
@@ -0,0 +1,855 @@
+
+using Serein.DbSql;
+using Newtonsoft.Json.Converters;
+using Newtonsoft.Json;
+using SqlSugar;
+using System.Data;
+using System.Linq.Expressions;
+using Serein.Tool;
+
+namespace Serein.Helper
+{
+
+ // public class RepositoryBase : DataBase, IRepositoryBase where TEntity : class, new()
+ public class RepositoryBase : IRepositoryBase where TEntity : class, new()
+ {
+ public bool isHaveErr;
+
+ public string ErrMsg = "";
+
+ public string filterName = "SubSystemName";
+ ~RepositoryBase()
+ {
+ DBSync.ReSetCrudDb();
+ }
+ public RepositoryBase()
+ {
+ }
+ ///
+ /// 是否优先使用本地数据库
+ ///
+ public bool IsUseLoaclDB = false;
+
+
+ #region 数据库操作 泛型抽象方法
+
+ #region 优先查询 主数据库
+
+ ///
+ /// 无状态数据操作(查询)泛型抽象方法
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual T SyncExecuteRead(Func func)
+ {
+ var syncSqlConfig = DBSync.GetSyncSqlConfig(); // 基类获取数据库配置
+ if (IsUseLoaclDB)
+ {
+ var secondaryDB = syncSqlConfig.GetSecondaryDB();
+ return func.Invoke(secondaryDB); // 尝试查询本地数据库
+ }
+
+
+ if (syncSqlConfig.GetNetworkState()) // 网络检测
+ {
+ try
+ {
+ var primaryDB = syncSqlConfig.GetPrimaryDB();
+ if (primaryDB != null)
+ {
+ return func.Invoke(primaryDB); // 尝试查询本地数据库
+ }
+ else
+ {
+ Console.WriteLine("远程数据库不可用");
+ }
+ }
+ catch(Exception ex)
+ {
+ DBSync.SetIsNeedSyncData(true); // 网络不可达
+ Console.WriteLine(ex.ToString());
+ }
+ }
+
+ try
+ {
+ var secondaryDB = syncSqlConfig.GetSecondaryDB();
+ return func.Invoke(secondaryDB); // 尝试查询本地数据库
+ }
+ catch
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, $"主从数据库不可用。\r\n {syncSqlConfig.ToString()} ");
+ }
+ }
+ ///
+ /// 无状态数据操作(查询)泛型抽象方法
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual T SyncExecuteRead(Func, T> func)
+ {
+ var syncSqlConfig = DBSync.GetSyncSqlConfig(); // 基类获取数据库配置
+
+ if (IsUseLoaclDB)
+ {
+ var secondaryDB = syncSqlConfig.GetSecondaryDB().GetSimpleClient();
+ return func.Invoke(secondaryDB); // 尝试查询本地数据库
+ }
+
+ if (syncSqlConfig.GetNetworkState()) // 网络检测
+ {
+ try
+ {
+ var primaryDB = syncSqlConfig.GetPrimaryDB()?.GetSimpleClient();
+ if (primaryDB != null)
+ {
+ return func.Invoke(primaryDB); // 尝试查询远程数据库
+ }
+ else
+ {
+ Console.WriteLine("远程数据库不可用");
+ }
+ }
+ catch (Exception ex)
+ {
+ DBSync.SetIsNeedSyncData(true); // 网络不可达
+ Console.WriteLine(ex.ToString());
+ }
+ }
+
+ try
+ {
+ var secondaryDB = syncSqlConfig.GetSecondaryDB().GetSimpleClient();
+ return func.Invoke(secondaryDB); // 尝试查询本地数据库
+ }
+ catch
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, $"主从数据库不可用。\r\n {syncSqlConfig.ToString()} ");
+ }
+ }
+
+ #endregion
+
+
+ #region 优先查询 从数据库 (已注释)
+ /* ///
+ /// 无状态数据操作(查询)泛型抽象方法
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual T ExecuteSyncOperation(Func func)
+ {
+ DBSync.SyncEvent.Wait();
+ var secondaryDB = SyncSqlConfig.GetSecondaryDB();
+
+ try
+ {
+ return func.Invoke(secondaryDB); // 优先尝试查询本地数据库
+ }
+ catch
+ {
+ try
+ {
+ var primaryDB = SyncSqlConfig.GetPrimaryDB();
+ if (primaryDB != null)
+ {
+ if (SyncSqlConfig.GetNetworkState()) // 网络检测
+ {
+ DBSync.SyncEvent.Wait();
+ return func.Invoke(primaryDB); // 尝试查询远程数据库
+ }
+ else
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, "网络不可达,无法查询远程数据库。");
+ }
+ }
+ else
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, "远程数据库不可用。");
+ }
+ }
+ catch
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, $"远程数据库查询失败。\r\n {SyncSqlConfig.ToString()} ");
+ }
+ }
+ }
+
+ ///
+ /// 无状态数据操作(查询)泛型抽象方法
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual T ExecuteSyncOperation(Func, T> func)
+ {
+ DBSync.SyncEvent.Wait();
+
+ var secondaryDB = SyncSqlConfig.GetSecondaryDB().GetSimpleClient();
+
+ try
+ {
+ return func.Invoke(secondaryDB); // 优先尝试查询本地数据库
+ }
+ catch
+ {
+ // 本地数据库查询失败,尝试远程数据库
+ try
+ {
+ var primaryDB = SyncSqlConfig.GetPrimaryDB()?.GetSimpleClient();
+ if (primaryDB != null)
+ {
+ if (SyncSqlConfig.GetNetworkState()) // 网络检测
+ {
+ DBSync.SyncEvent.Wait();
+ return func.Invoke(primaryDB); // 尝试查询远程数据库
+ }
+ else
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, "网络不可达,无法查询远程数据库。");
+ }
+ }
+ else
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, "远程数据库不可用。");
+ }
+ }
+ catch
+ {
+ throw new DBSyncException(DBSyncExType.CrudError, $"远程数据库查询失败。\r\n {SyncSqlConfig.ToString()} ");
+ }
+ }
+ }
+ */
+ #endregion
+
+ #region 增加、更新、删除 操作泛型方法
+ ///
+ /// 有状态数据操作(更新、增加、删除)泛型抽象方法,优先操作本地数据库,操作远程数据库失败时调用DBSync.SetIsNeedSyncData(true);
+ ///
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual T SyncExecuteCUD(Func func)
+ {
+ var syncSqlConfig = DBSync.GetSyncSqlConfig(); // 基类获取数据库配置
+ var secondaryDB = syncSqlConfig.GetSecondaryDB();
+ try
+ {
+ var secondaryResult = func.Invoke(secondaryDB); // 本地数据库操作
+ if (IsUseLoaclDB)
+ {
+ return secondaryResult;
+ }
+ if (syncSqlConfig.GetNetworkState()) // 网络检测
+ {
+ var primaryDB = syncSqlConfig.GetPrimaryDB();
+ if(primaryDB != null)
+ {
+ var primaryResult = func.Invoke(primaryDB); // 远程数据库操作
+ return primaryResult;
+ }
+ else
+ {
+ Console.WriteLine("远程数据库不可用");
+ }
+ }
+ return secondaryResult;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("主从数据库不可用:" + ex.ToString());
+ DBSync.SetIsNeedSyncData(true);
+ throw new DBSyncException(DBSyncExType.CrudError, $"主从数据库不可用。\r\n {syncSqlConfig.ToString()} ");
+ }
+ }
+
+ public virtual T SyncExecuteCUD(Func, T> func)
+ {
+ var syncSqlConfig = DBSync.GetSyncSqlConfig(); // 基类获取数据库配置
+ var secondaryDB = syncSqlConfig.GetSecondaryDB().GetSimpleClient();
+
+ try
+ {
+ var secondaryResult = func.Invoke(secondaryDB); // 本地数据库操作
+ if (IsUseLoaclDB)
+ {
+ return secondaryResult;
+ }
+ if (syncSqlConfig.GetNetworkState()) // 网络检测
+ {
+ var primaryDB = syncSqlConfig.GetPrimaryDB().GetSimpleClient();
+ if(primaryDB != null)
+ {
+ var primaryResult = func.Invoke(primaryDB); // 远程数据库操作
+ return primaryResult;
+ }
+ else
+ {
+ Console.WriteLine("远程数据库不可用");
+ }
+ }
+ return secondaryResult;
+ }
+ catch (Exception ex)
+ {
+ Console.WriteLine("主从数据库不可用:" + ex.ToString());
+ DBSync.SetIsNeedSyncData(true);
+ throw new DBSyncException(DBSyncExType.CrudError, $"主从数据库不可用。\r\n {syncSqlConfig.ToString()} ");
+ }
+ }
+
+
+ #endregion
+
+
+ public TEntity SyncRead(Func func)
+ {
+ return SyncExecuteRead(func);
+ }
+
+ public bool SyncRead(Func func)
+ {
+ return SyncExecuteRead(func);
+ }
+
+ public List SyncRead(Func> func)
+ {
+ return SyncExecuteRead(func);
+ }
+
+
+ ///
+ /// 查询返回实体
+ ///
+ public TEntity SyncRead(Func, TEntity> func)
+ {
+ return SyncExecuteRead(func);
+ }
+
+ ///
+ /// 查询返回实体列表
+ ///
+ public List SyncRead(Func, List> func)
+ {
+ return SyncExecuteRead(func);
+ }
+
+ public TEntity SyncCUD(Func func)
+ {
+ return SyncExecuteCUD(func);
+ }
+
+ public int SyncCUD(Func func)
+ {
+ return SyncExecuteCUD(func);
+ }
+
+ public bool SyncCUD(Func func)
+ {
+ return SyncExecuteCUD(func);
+ }
+
+ public TEntity SyncSimpleCUD(Func, TEntity> func)
+ {
+
+ return SyncExecuteCUD(func);
+ }
+
+ public int SyncSimpleCUD(Func, int> func)
+ {
+ return SyncExecuteCUD(func);
+ }
+
+ public bool SyncSimpleCUD(Func, bool> func)
+ {
+ return SyncExecuteCUD(func);
+ }
+
+
+ #endregion
+
+
+
+
+ public virtual TEntity GetModelByID(dynamic ID)
+ {
+ return SyncRead(db => db.GetById(ID));
+ }
+
+ public virtual TEntity GetModel(Expression> where)
+ {
+ try
+ {
+ return SyncRead(db => db.Queryable().Where(where).First()); //db.GetSingle(where));
+ // GetSingle结果不能大于1
+ }
+ catch (Exception ex)
+ {
+
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return null;
+ }
+ }
+
+
+ public virtual int Add(TEntity model)
+ {
+ try
+ {
+ return SyncCUD(db => db.Insertable(model).ExecuteCommand());
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return 0;
+ }
+ }
+
+
+ public virtual int AddAndReturnIndex(TEntity model)
+ {
+ try
+ {
+ return SyncCUD(db => db.Insertable(model).ExecuteReturnIdentity());
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return 0;
+ }
+ }
+
+
+ public virtual bool Exist(Expression> where)
+ {
+ try
+ {
+ return SyncRead(db => db.Queryable().Where(where).Take(1).Any());
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return false;
+ }
+ }
+
+
+ public int AddOrUpdate(TEntity model, string keyValue)
+ {
+ if (keyValue == "")
+ {
+ try
+ {
+ return SyncCUD(db => db.Insertable(model).ExecuteCommand());
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return 0;
+ }
+ }
+ return SyncCUD(db => db.Updateable(model).ExecuteCommand());
+ }
+
+
+ public virtual int Update(TEntity model)
+ {
+ return SyncCUD(db => db.Updateable(model).ExecuteCommand());
+ }
+
+
+ public virtual int UpdateColumns(TEntity model, Expression> expression)
+ {
+ //DatabaseSync.StartcaControls();
+ try
+ {
+ return SyncCUD(db => db.Updateable(model).UpdateColumns(expression).ExecuteCommand());
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return 0;
+ }
+ }
+
+
+ public virtual bool DeleteByID(dynamic ID)
+ {
+
+ //SyncCUD(db => db.Updateable().RemoveDataCache().ExecuteCommand());
+ return SyncSimpleCUD(db => (bool)db.DeleteById(ID));
+ }
+
+
+ public virtual bool Delete(Expression> where)
+ {
+ return SyncSimpleCUD(db => db.Delete(where));
+ }
+
+
+ public virtual string GetPageList(Pagination pagination, Expression> where = null)
+ {
+ //DatabaseSync.StartcaControls();
+ return new
+ {
+ rows = GetList(pagination, where),
+ total = pagination.total,
+ page = pagination.page,
+ records = pagination.records
+ }.ToJson();
+ }
+
+
+ public virtual TEntity GetSingle(Expression> expression)
+ {
+ //DatabaseSync.StartcaControls();
+ return SyncRead(db => db.Queryable().Filter(filterName, isDisabledGobalFilter: true).Single(expression));
+ }
+
+
+ public virtual List GetTop(int Top, Expression> expression, OrderByType _OrderByType = OrderByType.Asc, Expression> where = null, string selstr = "*")
+ {
+ return SyncRead(db => db.Queryable().Select(selstr).WhereIF(where != null, where)
+ .Take(Top)
+ .OrderBy(expression, _OrderByType)
+ .Filter(filterName, isDisabledGobalFilter: true)
+ .ToList());
+ }
+
+ ///
+ /// 排序表达式所用的键,排序方式,搜索条件
+ ///
+ ///
+ ///
+ ///
+ ///
+ public virtual TEntity GetFirst(Expression> OrderExpression, OrderByType _OrderByType = OrderByType.Asc, Expression> where = null)
+ {
+ return SyncRead(db => db.Queryable().Filter(filterName, isDisabledGobalFilter: true).WhereIF(where != null, where)
+ .OrderBy(OrderExpression, _OrderByType)
+ .First());
+ }
+
+ public virtual List GetList(Pagination pagination, Expression> where = null)
+ {
+ int totalNumber = 0;
+ List result = SyncRead(db => db.Queryable().WhereIF(where != null, where).OrderBy(pagination.sidx + " " + pagination.sord)
+ .Filter(filterName, isDisabledGobalFilter: true)
+ .ToPageList(pagination.page, pagination.rows, ref totalNumber));
+ pagination.records = totalNumber;
+ return result;
+ }
+
+
+ public virtual List GetList(Expression> where = null)
+ {
+ return SyncRead(db => db.Queryable().WhereIF(where != null, where).Filter(filterName, isDisabledGobalFilter: true)
+ .ToList());
+ }
+
+ public virtual List GetList()
+ {
+ return SyncRead(db => db.Queryable().ToList());
+ }
+
+
+ public virtual DataTable GetDataTable(Expression> where = null, Pagination pagination = null)
+ {
+ if (pagination != null)
+ {
+ return DataHelper.ListToDataTable(GetList(pagination, where));
+ }
+ return DataHelper.ListToDataTable(GetList(where));
+ }
+
+ public virtual void UseFilter(SqlFilterItem item)
+ {
+ SyncCUD(db =>
+ {
+ db.QueryFilter.Remove(item.FilterName);
+ db.QueryFilter.Add(item);
+ return 0;
+ });
+ filterName = item.FilterName;
+ }
+
+
+ public virtual void ClearFilter()
+ {
+ SyncCUD(db =>
+ {
+ db.QueryFilter.Clear();
+ return 0;
+ });
+
+ filterName = null;
+ }
+
+
+
+
+
+
+
+
+ /* public void ReSetConnStr(string constr, SqlSugar.DbType _dbtype)
+ {
+ db = DBHelper.CreateDB(constr, _dbtype);
+ Sclient = db.GetSimpleClient();
+ }
+
+
+ public virtual TEntity GetModelByID(dynamic ID)
+ {
+ return Sclient.GetById(ID);
+ }
+
+
+ public virtual TEntity GetModel(Expression> where)
+ {
+ try
+ {
+ return Sclient.GetSingle(where);
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return null;
+ }
+ }
+
+
+ public virtual int Add(TEntity model)
+ {
+ try
+ {
+ return db.Insertable(model).ExecuteCommand();
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return 0;
+ }
+ }
+
+
+ public virtual int AddAndReturnIndex(TEntity model)
+ {
+ try
+ {
+ return db.Insertable(model).ExecuteReturnIdentity();
+ }
+ catch (Exception ex)
+ {
+ isHaveErr = true;
+ ErrMsg = ex.Message;
+ return 0;
+ }
+ }
+
+
+ public virtual bool Exist(Expression> where)
+ {
+ return db.Queryable().Where(where).Take(1).Any();
+ }
+
+
+ public int AddOrUpdate(TEntity model, string Keyvale)
+ {
+ if (Keyvale == "")
+ {
+ return db.Insertable(model).ExecuteCommand();
+ }
+ return db.Updateable(model).ExecuteCommand();
+ }
+
+
+ public virtual int Update(TEntity model)
+ {
+ return db.Updateable(model).ExecuteCommand();
+ }
+
+
+ public virtual int UpdateColumns(TEntity model, Expression> expression)
+ {
+ return db.Updateable(model).UpdateColumns(expression).ExecuteCommand();
+ }
+
+
+ public virtual bool DeleteByID(dynamic ID)
+ {
+ db.Updateable().RemoveDataCache().ExecuteCommand();
+ return Sclient.DeleteById(ID);
+ }
+
+
+ public virtual bool Delete(Expression> where)
+ {
+ return Sclient.Delete(where);
+ }
+
+
+ public virtual string GetPageList(Pagination pagination, Expression> where = null)
+ {
+ return new
+ {
+ rows = GetList(pagination, where),
+ total = pagination.total,
+ page = pagination.page,
+ records = pagination.records
+ }.ToJson();
+ }
+
+
+ public virtual TEntity GetSingle(Expression> expression)
+ {
+ return db.Queryable().Filter(filterName, isDisabledGobalFilter: true).Single(expression);
+ }
+
+
+ public virtual List GetTop(int Top, Expression> expression, OrderByType _OrderByType = OrderByType.Asc, Expression> where = null, string selstr = "*")
+ {
+ return db.Queryable().Select(selstr).WhereIF(where != null, where)
+ .Take(Top)
+ .OrderBy(expression, _OrderByType)
+ .Filter(filterName, isDisabledGobalFilter: true)
+ .ToList();
+ }
+
+
+ public virtual TEntity GetFirst(Expression> OrderExpression, OrderByType _OrderByType = OrderByType.Asc, Expression> where = null)
+ {
+ return db.Queryable().Filter(filterName, isDisabledGobalFilter: true).WhereIF(where != null, where)
+ .OrderBy(OrderExpression, _OrderByType)
+ .First();
+ }
+
+ public virtual List GetList(Pagination pagination, Expression> where = null)
+ {
+ int totalNumber = 0;
+ List result = db.Queryable().WhereIF(where != null, where).OrderBy(pagination.sidx + " " + pagination.sord)
+ .Filter(filterName, isDisabledGobalFilter: true)
+ .ToPageList(pagination.page, pagination.rows, ref totalNumber);
+ pagination.records = totalNumber;
+ return result;
+ }
+
+
+ public virtual List GetList(Expression> where = null)
+ {
+ return db.Queryable().WhereIF(where != null, where).Filter(filterName, isDisabledGobalFilter: true)
+ .ToList();
+ }
+
+ public virtual List GetList()
+ {
+ return db.Queryable().ToList();
+ }
+
+
+ public virtual DataTable GetDataTable(Expression> where = null, Pagination pagination = null)
+ {
+ if (pagination != null)
+ {
+ return DataHelper.ListToDataTable(GetList(pagination, where));
+ }
+ return DataHelper.ListToDataTable(GetList(where));
+ }
+
+ public virtual void UseFilter(SqlFilterItem item)
+ {
+ db.QueryFilter.Remove(item.FilterName);
+ db.QueryFilter.Add(item);
+ filterName = item.FilterName;
+ }
+
+
+ public virtual void ClearFilter()
+ {
+ db.QueryFilter.Clear();
+ filterName = null;
+ }
+
+ public void BeginTran()
+ {
+ db.Ado.BeginTran();
+ }
+
+ public void CommitTran()
+ {
+ db.Ado.CommitTran();
+ }
+
+ public void RollbackTran()
+ {
+ db.Ado.RollbackTran();
+ }*/
+ }
+ public class Pagination
+ {
+ ///
+ /// 每页行数
+ ///
+ public int rows { get; set; }
+
+ ///
+ /// 当前页
+ ///
+ public int page { get; set; }
+
+ ///
+ /// /排序列
+ ///
+ public string sidx { get; set; }
+
+ ///
+ /// 排序类型
+ ///
+ public string sord { get; set; }
+
+ ///
+ /// 总记录数
+ ///
+ public int records { get; set; }
+
+ ///
+ /// 总页数
+ ///
+ public int total
+ {
+ get
+ {
+ if (records > 0)
+ {
+ if (records % rows != 0)
+ {
+ return records / rows + 1;
+ }
+
+ return records / rows;
+ }
+ return 0;
+ }
+ }
+ }
+
+
+}
diff --git a/Library/DynamicFlow/Api.cs b/Library/DynamicFlow/Api.cs
new file mode 100644
index 0000000..a723376
--- /dev/null
+++ b/Library/DynamicFlow/Api.cs
@@ -0,0 +1,13 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.DynamicFlow
+{
+ public interface IDynamicFlowNode
+ {
+ }
+
+}
diff --git a/Library/DynamicFlow/Attribute.cs b/Library/DynamicFlow/Attribute.cs
new file mode 100644
index 0000000..4ed8556
--- /dev/null
+++ b/Library/DynamicFlow/Attribute.cs
@@ -0,0 +1,73 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Serein.DynamicFlow
+{
+
+ public enum DynamicNodeType
+ {
+ ///
+ /// 初始化
+ ///
+ Init,
+ ///
+ /// 开始载入
+ ///
+ Loading,
+ ///
+ /// 结束
+ ///
+ Exit,
+
+ ///
+ /// 触发器
+ ///
+ Flipflop,
+ ///
+ /// 条件节点
+ ///
+ Condition,
+ ///
+ /// 动作节点
+ ///
+ Action,
+ }
+
+
+
+ ///
+ /// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
+ ///
+ [AttributeUsage(AttributeTargets.Class)]
+ public class DynamicFlowAttribute(bool scan = true) : Attribute
+ {
+ public bool Scan { get; set; } = scan;
+ }
+
+ ///
+ /// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
+ ///
+ [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
+ public class MethodDetailAttribute(DynamicNodeType methodDynamicType,
+ string methodTips = "",
+ bool scan = true,
+ string lockName = "") : Attribute
+ {
+ public bool Scan { get; set; } = scan;
+ public string MethodTips { get; } = methodTips;
+ public DynamicNodeType MethodDynamicType { get; } = methodDynamicType;
+ public string LockName { get; } = lockName;
+ }
+
+ ///
+ /// 是否为显式参数
+ ///
+ [AttributeUsage(AttributeTargets.Parameter)]
+ public class ExplicitAttribute : Attribute // where TEnum : Enum
+ {
+ }
+
+}
diff --git a/Library/DynamicFlow/DynamicContext.cs b/Library/DynamicFlow/DynamicContext.cs
new file mode 100644
index 0000000..82c9ffa
--- /dev/null
+++ b/Library/DynamicFlow/DynamicContext.cs
@@ -0,0 +1,165 @@
+using DynamicDemo.Node;
+using System;
+using System.Collections.Concurrent;
+using System.Collections.Generic;
+using System.Linq;
+using System.Runtime.CompilerServices;
+using System.Text;
+using System.Threading.Tasks;
+using static System.Collections.Specialized.BitVector32;
+using static System.Runtime.InteropServices.JavaScript.JSType;
+
+namespace Serein.DynamicFlow
+{
+
+ public enum FfState
+ {
+ Succeed,
+ Cancel,
+ }
+ ///
+ /// 触发器上下文
+ ///
+ public class FlipflopContext
+ {
+ public FfState State { get; set; }
+ public object? Data { get; set; }
+ /*public FlipflopContext()
+ {
+ State = FfState.Cancel;
+ }*/
+ public FlipflopContext(FfState ffState,object? data = null)
+ {
+ State = ffState;
+ Data = data;
+ }
+ }
+
+
+ ///
+ /// 动态流程上下文
+ ///
+ public class DynamicContext(IServiceContainer serviceContainer)
+ {
+
+ private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();
+
+ public IServiceContainer ServiceContainer { get; } = serviceContainer;
+ private List InitServices { get; set; } = [];
+
+ // private ConcurrentDictionary ContextData { get; set; } = [];
+
+ //public void SetFlowData(object data)
+ //{
+ // var threadId = Thread.CurrentThread.ManagedThreadId.ToString();
+ // var name = $"{threadId}.{contextGuid}FlowData";
+ // SetData(name,data);
+ //}
+ //public object GetFlowData(bool IsRetain = false)
+ //{
+ // var threadId = Thread.CurrentThread.ManagedThreadId.ToString();
+ // var name = $"{threadId}.{contextGuid}FlowData";
+ // if (IsRetain)
+ // {
+ // return GetData(name);
+ // }
+ // else
+ // {
+ // return GetAndRemoteData(name);
+
+ // }
+ //}
+
+
+ public void InitService()
+ {
+ InitService(typeof(T));
+ }
+ public void InitService(Type type)
+ {
+ if (!InitServices.Contains(type))
+ {
+ InitServices.Add(type);
+ }
+ else
+ {
+ //throw new Exception("初始化时试图添加已存在的类型:"+type.Name);
+ Console.WriteLine("初始化时试图添加已存在的类型:" + type.Name);
+ }
+ }
+ public void Biuld()
+ {
+ foreach (var item in InitServices)
+ {
+ ServiceContainer.Register(item);
+ }
+ ServiceContainer.Build();
+ }
+
+ //public object? RemoveData(string key)
+ //{
+ // if (ContextData.Remove(key, out var data))
+ // {
+ // return data;
+ // }
+ // return null;
+ //}
+
+ //public void SetData(string key, T value)
+ //{
+ // ContextData[key] = value;
+ //}
+
+ //public T? GetData(string key)
+ //{
+ // if (ContextData.TryGetValue(key, out object? value))
+ // {
+ // if(value == null)
+ // {
+ // return default;
+ // }
+ // if (value.GetType() == typeof(T))
+ // {
+ // return (T)value;
+ // }
+
+ // }
+ // return default;
+ //}
+
+ //public object? GetData(string key)
+ //{
+ // if (ContextData.TryGetValue(key, out object? value))
+ // {
+ // return value;
+ // }
+ // return null;
+ //}
+
+
+ //public ConcurrentDictionary FlipFlopTasks { get; set; } = [];
+
+ public NodeRunTcs NodeRunCts { get; set; }
+ public Task CreateTimingTask(Action action, int time = 100, int count = -1)
+ {
+ NodeRunCts ??= ServiceContainer.Get();
+ return Task.Factory.StartNew(async () =>
+ {
+ for(int i = 0; i < count; i++)
+ {
+ NodeRunCts.Token.ThrowIfCancellationRequested();
+ await time;
+ action.Invoke();
+ }
+ });
+ }
+ }
+
+ public static class MyExtensions
+ {
+ public static TaskAwaiter GetAwaiter(this int i) => Task.Delay(i).GetAwaiter();
+ }
+
+
+ // if (time <= 0) throw new ArgumentException("时间不能≤0");
+}
diff --git a/Library/DynamicFlow/MethodDetails.cs b/Library/DynamicFlow/MethodDetails.cs
new file mode 100644
index 0000000..bc0e99b
--- /dev/null
+++ b/Library/DynamicFlow/MethodDetails.cs
@@ -0,0 +1,218 @@
+using System;
+using System.Collections.Generic;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+using static Dm.net.buffer.ByteArrayBuffer;
+
+namespace Serein.DynamicFlow
+{
+ ///
+ /// 显式参数
+ ///
+ public class ExplicitData
+ {
+ ///
+ /// 索引
+ ///
+ public int Index { get; set; }
+ ///
+ /// 是否为显式参数
+ ///
+ public bool IsExplicitData { get; set; }
+ ///
+ /// 显式类型
+ ///
+ public Type? ExplicitType { get; set; }
+
+ ///
+ /// 显示类型编号>
+ ///
+ public string ExplicitTypeName { get; set; }
+
+ ///
+ /// 方法需要的类型
+ ///
+ public Type DataType { get; set; }
+ ///
+ /// 方法入参参数名称
+ ///
+ public string ParameterName { get; set; }
+ ///
+ /// 入参值
+ ///
+ public string DataValue { get; set; }
+
+ public string[] Items { get; set; }
+
+
+
+ public ExplicitData Clone() => new()
+ {
+ Index = Index,
+ IsExplicitData = IsExplicitData,
+ ExplicitType = ExplicitType,
+ DataType = DataType,
+ ParameterName = ParameterName,
+ ExplicitTypeName = ExplicitTypeName,
+ DataValue = string.IsNullOrEmpty(DataValue) ? string.Empty : DataValue,
+ Items = [.. Items],
+ };
+ }
+
+
+
+ public class MethodDetails
+ {
+ public MethodDetails CpoyNew()
+ {
+ return new MethodDetails
+ {
+ ActingInstance = ActingInstance,
+ ActingInstanceType = ActingInstanceType,
+ MethodDelegate = MethodDelegate,
+ MethodDynamicType = MethodDynamicType,
+ MethodGuid = Guid.NewGuid().ToString(),
+ MethodTips = MethodTips + " Cpoy",
+ //ParameterTypes = ParameterTypes,
+ ReturnType = ReturnType,
+ MethodName = MethodName,
+ MethodLockName = MethodLockName,
+ //ExplicitDataValues = ExplicitDataValues.Select(it => "").ToArray(),
+ ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
+ //IsExplicits = IsExplicits,
+ };
+ }
+
+ ///
+ /// 作用实例
+ ///
+ public Type ActingInstanceType { get; set; }
+ ///
+ /// 作用实例
+ ///
+ public object ActingInstance { get; set; }
+ ///
+ /// 方法GUID
+ ///
+ public string MethodGuid { get; set; }
+ ///
+ /// 方法名称
+ ///
+ public string MethodName { get; set; }
+ ///
+ /// 方法委托
+ ///
+ public Delegate MethodDelegate { get; set; }
+ ///
+ /// 节点类型
+ ///
+ public DynamicNodeType MethodDynamicType { get; set; }
+ ///
+ /// 锁名称
+ ///
+ public string MethodLockName { get; set; }
+
+ ///
+ /// 方法说明
+ ///
+ public string MethodTips { get; set; }
+
+ ///
+ /// 参数内容
+ ///
+ public ExplicitData[] ExplicitDatas { get; set; }
+
+
+ ///
+ /// 出参类型
+ ///
+ public Type ReturnType { get; set; }
+
+
+
+
+
+ public bool IsCanConnect(Type returnType)
+ {
+ if (ExplicitDatas.Length == 0)
+ {
+ // 目标不需要传参,可以舍弃结果?
+ return true;
+ }
+ var types = ExplicitDatas.Select(it => it.DataType).ToArray();
+ // 检查返回类型是否是元组类型
+ if (returnType.IsGenericType && IsValueTuple(returnType))
+ {
+
+ return CompareGenericArguments(returnType, types);
+ }
+ else
+ {
+ int index = 0;
+ if (types[index] == typeof(DynamicContext))
+ {
+ index++;
+ if (types.Length == 1)
+ {
+ return true;
+ }
+ }
+ // 被连接节点检查自己需要的参数类型,与发起连接的节点比较返回值类型
+ if (returnType == types[index])
+ {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ ///
+ /// 检查元组类型
+ ///
+ ///
+ ///
+ private bool IsValueTuple(Type type)
+ {
+ if (!type.IsGenericType) return false;
+
+ var genericTypeDef = type.GetGenericTypeDefinition();
+ return genericTypeDef == typeof(ValueTuple<>) ||
+ genericTypeDef == typeof(ValueTuple<,>) ||
+ genericTypeDef == typeof(ValueTuple<,,>) ||
+ genericTypeDef == typeof(ValueTuple<,,,>) ||
+ genericTypeDef == typeof(ValueTuple<,,,,>) ||
+ genericTypeDef == typeof(ValueTuple<,,,,,>) ||
+ genericTypeDef == typeof(ValueTuple<,,,,,,>) ||
+ genericTypeDef == typeof(ValueTuple<,,,,,,,>);
+ }
+
+ private bool CompareGenericArguments(Type returnType, Type[] parameterTypes)
+ {
+ var genericArguments = returnType.GetGenericArguments();
+ var length = parameterTypes.Length;
+
+ for (int i = 0; i < genericArguments.Length; i++)
+ {
+ if (i >= length) return false;
+
+ if (IsValueTuple(genericArguments[i]))
+ {
+ // 如果当前参数也是 ValueTuple,递归检查嵌套的泛型参数
+ if (!CompareGenericArguments(genericArguments[i], parameterTypes.Skip(i).ToArray()))
+ {
+ return false;
+ }
+ }
+ else if (genericArguments[i] != parameterTypes[i])
+ {
+ return false;
+ }
+ }
+
+ return true;
+ }
+ }
+
+
+}
diff --git a/Library/DynamicFlow/NodeFlowStarter.cs b/Library/DynamicFlow/NodeFlowStarter.cs
new file mode 100644
index 0000000..bcad046
--- /dev/null
+++ b/Library/DynamicFlow/NodeFlowStarter.cs
@@ -0,0 +1,158 @@
+using Serein;
+using Serein.DynamicFlow;
+using Serein.DynamicFlow.NodeModel;
+using Serein.DynamicFlow.Tool;
+using Serein.Web;
+using SqlSugar;
+using System.Collections.Concurrent;
+
+namespace DynamicDemo.Node
+{
+
+ public class NodeRunTcs: CancellationTokenSource
+ {
+
+ }
+
+ public class NodeFlowStarter(IServiceContainer serviceContainer,List methodDetails)
+ {
+ private readonly IServiceContainer ServiceContainer = serviceContainer;
+ private readonly List methodDetails = methodDetails;
+ private Action ExitAction = null;
+ private DynamicContext context = null;
+
+ public NodeRunTcs MainCts;
+
+ ///
+ /// 运行测试
+ ///
+ ///
+ ///
+ public async Task RunAsync(List nodes)
+ {
+ var startNode = nodes.FirstOrDefault(p => p.IsStart);
+ if (startNode == null) { return; }
+ context = new(ServiceContainer);
+
+ MainCts = ServiceContainer.CreateServiceInstance();
+
+ var initMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Init).ToList();
+ var loadingMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Loading).ToList();
+ var exitMethods = methodDetails.Where(it => it.MethodDynamicType == DynamicNodeType.Exit).ToList();
+ ExitAction = () =>
+ {
+ ServiceContainer.Run((web) =>
+ {
+ web?.Stop();
+ });
+ foreach (MethodDetails? md in exitMethods)
+ {
+ object?[]? args = [context];
+ object?[]? data = [md.ActingInstance, args];
+ md.MethodDelegate.DynamicInvoke(data);
+ }
+ if(context != null && context.NodeRunCts != null && !context.NodeRunCts.IsCancellationRequested)
+ {
+ context.NodeRunCts.Cancel();
+ }
+ if (MainCts!=null && !MainCts.IsCancellationRequested) MainCts.Cancel();
+ ServiceContainer.Reset();
+ };
+
+
+ foreach (var md in initMethods) // 初始化 - 调用方法
+ {
+ //md.ActingInstance = context.ServiceContainer.Get(md.ActingInstanceType);
+ object?[]? args = [context];
+ object?[]? data = [md.ActingInstance, args];
+ md.MethodDelegate.DynamicInvoke(data);
+ }
+ context.Biuld();
+
+ foreach (var md in loadingMethods) // 加载
+ {
+ //md.ActingInstance = context.ServiceContainer.Get(md.ActingInstanceType);
+ object?[]? args = [context];
+ object?[]? data = [md.ActingInstance, args];
+ md.MethodDelegate.DynamicInvoke(data);
+ }
+
+ var flipflopNodes = nodes.Where(it => it.MethodDetails?.MethodDynamicType == DynamicNodeType.Flipflop
+ && it.PreviousNodes.Count == 0
+ && it.IsStart != true).ToArray();
+
+ var singleFlipflopNodes = flipflopNodes.Select(it => (SingleFlipflopNode)it).ToArray();
+
+ // 使用 TaskCompletionSource 创建未启动的任务
+ var tasks = singleFlipflopNodes.Select(async node =>
+ {
+ await FlipflopExecute(node);
+ }).ToArray();
+
+
+
+ try
+ {
+ await Task.WhenAll([startNode.ExecuteStack(context),.. tasks]);
+ }
+ catch (Exception ex)
+ {
+ await Console.Out.WriteLineAsync(ex.ToString());
+ }
+
+ }
+
+ private async Task FlipflopExecute(SingleFlipflopNode singleFlipFlopNode)
+ {
+ DynamicContext context = new DynamicContext(ServiceContainer);
+ MethodDetails md = singleFlipFlopNode.MethodDetails;
+
+ try
+ {
+ if (!DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
+ {
+ return;
+ }
+ var func = md.ExplicitDatas.Length == 0 ? ((Func