mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-04-29 02:53:23 +08:00
修改了IOC的实现,删除了项目排除的文件
This commit is contained in:
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Flow
|
|
||||||
{
|
|
||||||
public interface IDynamicFlowNode
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Flow
|
|
||||||
{
|
|
||||||
|
|
||||||
public enum DynamicNodeType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化
|
|
||||||
/// </summary>
|
|
||||||
Init,
|
|
||||||
/// <summary>
|
|
||||||
/// 开始载入
|
|
||||||
/// </summary>
|
|
||||||
Loading,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束
|
|
||||||
/// </summary>
|
|
||||||
Exit,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器
|
|
||||||
/// </summary>
|
|
||||||
Flipflop,
|
|
||||||
/// <summary>
|
|
||||||
/// 条件节点
|
|
||||||
/// </summary>
|
|
||||||
Condition,
|
|
||||||
/// <summary>
|
|
||||||
/// 动作节点
|
|
||||||
/// </summary>
|
|
||||||
Action,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public class DynamicFlowAttribute(bool scan = true) : Attribute
|
|
||||||
{
|
|
||||||
public bool Scan { get; set; } = scan;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
|
||||||
/// </summary>
|
|
||||||
[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为显式参数
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
public class ExplicitAttribute : Attribute // where TEnum : Enum
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,168 +0,0 @@
|
|||||||
using DynamicDemo.Node;
|
|
||||||
using Serein.Library.IOC;
|
|
||||||
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.Flow
|
|
||||||
{
|
|
||||||
|
|
||||||
public enum FfState
|
|
||||||
{
|
|
||||||
Succeed,
|
|
||||||
Cancel,
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器上下文
|
|
||||||
/// </summary>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态流程上下文
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public class DynamicContext(IServiceContainer serviceContainer)
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();
|
|
||||||
|
|
||||||
public IServiceContainer ServiceContainer { get; } = serviceContainer;
|
|
||||||
private List<Type> InitServices { get; set; } = [];
|
|
||||||
|
|
||||||
// private ConcurrentDictionary<string, object?> 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<T>()
|
|
||||||
{
|
|
||||||
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<T>(string key, T value)
|
|
||||||
//{
|
|
||||||
// ContextData[key] = value;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public T? GetData<T>(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<string,Task> FlipFlopTasks { get; set; } = [];
|
|
||||||
|
|
||||||
public NodeRunTcs NodeRunCts { get; set; }
|
|
||||||
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
|
||||||
{
|
|
||||||
NodeRunCts ??= ServiceContainer.Get<NodeRunTcs>();
|
|
||||||
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");
|
|
||||||
}
|
|
||||||
@@ -1,240 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static Dm.net.buffer.ByteArrayBuffer;
|
|
||||||
|
|
||||||
namespace Serein.Flow
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 显式参数
|
|
||||||
/// </summary>
|
|
||||||
public class ExplicitData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 索引
|
|
||||||
/// </summary>
|
|
||||||
public int Index { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为显式参数
|
|
||||||
/// </summary>
|
|
||||||
public bool IsExplicitData { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 显式类型
|
|
||||||
/// </summary>
|
|
||||||
public Type? ExplicitType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显示类型编号>
|
|
||||||
/// </summary>
|
|
||||||
public string ExplicitTypeName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法需要的类型
|
|
||||||
/// </summary>
|
|
||||||
public Type DataType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法入参参数名称
|
|
||||||
/// </summary>
|
|
||||||
public string ParameterName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 入参值
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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 Clone()
|
|
||||||
{
|
|
||||||
return new MethodDetails
|
|
||||||
{
|
|
||||||
ActingInstance = ActingInstance,
|
|
||||||
ActingInstanceType = ActingInstanceType,
|
|
||||||
MethodDelegate = MethodDelegate,
|
|
||||||
MethodDynamicType = MethodDynamicType,
|
|
||||||
MethodGuid = Guid.NewGuid().ToString(),
|
|
||||||
MethodTips = MethodTips ,
|
|
||||||
ReturnType = ReturnType,
|
|
||||||
MethodName = MethodName,
|
|
||||||
MethodLockName = MethodLockName,
|
|
||||||
|
|
||||||
ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 作用实例
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public Type ActingInstanceType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 作用实例
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public object ActingInstance { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法GUID
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodGuid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法名称
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法委托
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public Delegate MethodDelegate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 节点类型
|
|
||||||
/// </summary>
|
|
||||||
public DynamicNodeType MethodDynamicType { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 锁名称
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodLockName { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法说明
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodTips { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 参数内容
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public ExplicitData[] ExplicitDatas { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 出参类型
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查元组类型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,171 +0,0 @@
|
|||||||
using Serein.Flow;
|
|
||||||
using Serein.Flow.NodeModel;
|
|
||||||
using Serein.Flow.Tool;
|
|
||||||
using Serein.Library.Http;
|
|
||||||
using Serein.Library.IOC;
|
|
||||||
using SqlSugar;
|
|
||||||
|
|
||||||
namespace DynamicDemo.Node
|
|
||||||
{
|
|
||||||
|
|
||||||
public class NodeRunTcs: CancellationTokenSource
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public class NodeFlowStarter(IServiceContainer serviceContainer,List<MethodDetails> methodDetails)
|
|
||||||
|
|
||||||
{
|
|
||||||
private readonly IServiceContainer ServiceContainer = serviceContainer;
|
|
||||||
private readonly List<MethodDetails> methodDetails = methodDetails;
|
|
||||||
|
|
||||||
private Action ExitAction = null;
|
|
||||||
|
|
||||||
|
|
||||||
private DynamicContext context = null;
|
|
||||||
|
|
||||||
|
|
||||||
public NodeRunTcs MainCts;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行测试
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="sender"></param>
|
|
||||||
/// <param name="e"></param>
|
|
||||||
//public async Task RunAsync1(List<NodeBase> nodes)
|
|
||||||
//{
|
|
||||||
// await Task.Run(async ()=> await StartRunAsync(nodes));
|
|
||||||
//}
|
|
||||||
|
|
||||||
public async Task RunAsync(List<NodeBase> nodes)
|
|
||||||
{
|
|
||||||
var startNode = nodes.FirstOrDefault(p => p.IsStart);
|
|
||||||
if (startNode == null) { return; }
|
|
||||||
context = new(ServiceContainer);
|
|
||||||
|
|
||||||
MainCts = ServiceContainer.CreateServiceInstance<NodeRunTcs>();
|
|
||||||
|
|
||||||
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<WebServer>((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<object, object, Task<FlipflopContext>>)del) : ((Func<object, object[], Task<FlipflopContext>>)del);
|
|
||||||
|
|
||||||
while (!MainCts.IsCancellationRequested) // 循环中直到栈为空才会退出
|
|
||||||
{
|
|
||||||
object?[]? parameters = singleFlipFlopNode.GetParameters(context, md);
|
|
||||||
// 调用委托并获取结果
|
|
||||||
|
|
||||||
FlipflopContext flipflopContext = await func.Invoke(md.ActingInstance, parameters);
|
|
||||||
|
|
||||||
|
|
||||||
if (flipflopContext == null)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (flipflopContext.State == FfState.Cancel)
|
|
||||||
{
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
else if (flipflopContext.State == FfState.Succeed)
|
|
||||||
{
|
|
||||||
singleFlipFlopNode.FlowState = true;
|
|
||||||
singleFlipFlopNode.FlowData = flipflopContext.Data;
|
|
||||||
var tasks = singleFlipFlopNode.SucceedBranch.Select(nextNode =>
|
|
||||||
{
|
|
||||||
var context = new DynamicContext(ServiceContainer);
|
|
||||||
nextNode.PreviousNode = singleFlipFlopNode;
|
|
||||||
return nextNode.ExecuteStack(context);
|
|
||||||
}).ToArray();
|
|
||||||
Task.WaitAll(tasks);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
await Console.Out.WriteLineAsync(ex.ToString());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void Exit()
|
|
||||||
{
|
|
||||||
ExitAction?.Invoke();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 组合动作节点(用于动作区域)
|
|
||||||
/// </summary>
|
|
||||||
public class CompositeActionNode : NodeBase
|
|
||||||
{
|
|
||||||
public List<SingleActionNode> ActionNodes;
|
|
||||||
/// <summary>
|
|
||||||
/// 组合动作节点(用于动作区域)
|
|
||||||
/// </summary>
|
|
||||||
public CompositeActionNode(List<SingleActionNode> actionNodes)
|
|
||||||
{
|
|
||||||
ActionNodes = actionNodes;
|
|
||||||
}
|
|
||||||
public void AddNode(SingleActionNode node)
|
|
||||||
{
|
|
||||||
ActionNodes.Add(node);
|
|
||||||
MethodDetails ??= node.MethodDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
//public override void Execute(DynamicContext context)
|
|
||||||
//{
|
|
||||||
// //Dictionary<int,object> dict = new Dictionary<int,object>();
|
|
||||||
// for (int i = 0; i < ActionNodes.Count; i++)
|
|
||||||
// {
|
|
||||||
// SingleActionNode? action = ActionNodes[i];
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// action.Execute(context);
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// Debug.Write(ex.Message);
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// CurrentState = true;
|
|
||||||
// return;
|
|
||||||
|
|
||||||
|
|
||||||
// /*foreach (var nextNode in TrueBranchNextNodes)
|
|
||||||
// {
|
|
||||||
// nextNode.ExecuteStack(context);
|
|
||||||
// }*/
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,67 +0,0 @@
|
|||||||
|
|
||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 组合条件节点(用于条件区域)
|
|
||||||
/// </summary>
|
|
||||||
public class CompositeConditionNode : NodeBase
|
|
||||||
{
|
|
||||||
public List<SingleConditionNode> ConditionNodes { get; } =[];
|
|
||||||
|
|
||||||
|
|
||||||
public void AddNode(SingleConditionNode node)
|
|
||||||
{
|
|
||||||
ConditionNodes.Add(node);
|
|
||||||
MethodDetails ??= node.MethodDetails;
|
|
||||||
}
|
|
||||||
|
|
||||||
public override object? Execute(DynamicContext context)
|
|
||||||
{
|
|
||||||
// bool allTrue = ConditionNodes.All(condition => Judge(context,condition.MethodDetails));
|
|
||||||
// bool IsAllTrue = true; // 初始化为 true
|
|
||||||
FlowState = true;
|
|
||||||
foreach (SingleConditionNode? node in ConditionNodes)
|
|
||||||
{
|
|
||||||
if (!Judge(context, node))
|
|
||||||
{
|
|
||||||
FlowState = false;
|
|
||||||
break;// 一旦发现条件为假,立即退出循环
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return PreviousNode?.FlowData;
|
|
||||||
//if (IsAllTrue)
|
|
||||||
//{
|
|
||||||
// foreach (var nextNode in TrueBranchNextNodes)
|
|
||||||
// {
|
|
||||||
// nextNode.ExecuteStack(context);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// foreach (var nextNode in FalseBranchNextNodes)
|
|
||||||
// {
|
|
||||||
// nextNode.ExecuteStack(context);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
private bool Judge(DynamicContext context, SingleConditionNode node)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
node.Execute(context);
|
|
||||||
return node.FlowState;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.Message);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
public class CompositeLoopNode : NodeBase
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,510 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Serein.Flow.Tool;
|
|
||||||
|
|
||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
|
|
||||||
public enum ConnectionType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 真分支
|
|
||||||
/// </summary>
|
|
||||||
IsSucceed,
|
|
||||||
/// <summary>
|
|
||||||
/// 假分支
|
|
||||||
/// </summary>
|
|
||||||
IsFail,
|
|
||||||
/// <summary>
|
|
||||||
/// 异常发生分支
|
|
||||||
/// </summary>
|
|
||||||
IsError,
|
|
||||||
/// <summary>
|
|
||||||
/// 上游分支(执行当前节点前会执行一次上游分支)
|
|
||||||
/// </summary>
|
|
||||||
Upstream,
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
|
||||||
/// </summary>
|
|
||||||
public abstract class NodeBase : IDynamicFlowNode
|
|
||||||
{
|
|
||||||
|
|
||||||
public MethodDetails MethodDetails { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public string Guid { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
|
|
||||||
public bool IsStart { get; set; }
|
|
||||||
|
|
||||||
public string DelegateName { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行时的上一节点
|
|
||||||
/// </summary>
|
|
||||||
public NodeBase? PreviousNode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上一节点集合
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeBase> PreviousNodes { get; set; } = [];
|
|
||||||
/// <summary>
|
|
||||||
/// 下一节点集合(真分支)
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeBase> SucceedBranch { get; set; } = [];
|
|
||||||
/// <summary>
|
|
||||||
/// 下一节点集合(假分支)
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeBase> FailBranch { get; set; } = [];
|
|
||||||
/// <summary>
|
|
||||||
/// 异常分支
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeBase> ErrorBranch { get; set; } = [];
|
|
||||||
/// <summary>
|
|
||||||
/// 上游分支
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeBase> UpstreamBranch { get; set; } = [];
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前状态(进入真分支还是假分支,异常分支在异常中确定)
|
|
||||||
/// </summary>
|
|
||||||
public bool FlowState { get; set; } = true;
|
|
||||||
//public ConnectionType NextType { get; set; } = ConnectionType.IsTrue;
|
|
||||||
/// <summary>
|
|
||||||
/// 当前传递数据
|
|
||||||
/// </summary>
|
|
||||||
public object? FlowData { get; set; } = null;
|
|
||||||
|
|
||||||
|
|
||||||
// 正常流程节点调用
|
|
||||||
public virtual object? Execute(DynamicContext context)
|
|
||||||
{
|
|
||||||
MethodDetails md = MethodDetails;
|
|
||||||
object? result = null;
|
|
||||||
|
|
||||||
if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
{
|
|
||||||
if (md.ExplicitDatas.Length == 0)
|
|
||||||
{
|
|
||||||
if (md.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
object?[]? parameters = GetParameters(context, MethodDetails);
|
|
||||||
if (md.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// context.SetFlowData(result);
|
|
||||||
// CurrentData = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 触发器调用
|
|
||||||
public virtual async Task<object?> ExecuteAsync(DynamicContext context)
|
|
||||||
{
|
|
||||||
MethodDetails md = MethodDetails;
|
|
||||||
object? result = null;
|
|
||||||
|
|
||||||
if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
{
|
|
||||||
if (md.ExplicitDatas.Length == 0)
|
|
||||||
{
|
|
||||||
// 调用委托并获取结果
|
|
||||||
FlipflopContext flipflopContext = await ((Func<object, Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance);
|
|
||||||
|
|
||||||
if (flipflopContext != null)
|
|
||||||
{
|
|
||||||
if (flipflopContext.State == FfState.Cancel)
|
|
||||||
{
|
|
||||||
throw new Exception("this async task is cancel.");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (flipflopContext.State == FfState.Succeed)
|
|
||||||
{
|
|
||||||
FlowState = true;
|
|
||||||
result = flipflopContext.Data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FlowState = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
object?[]? parameters = GetParameters(context, MethodDetails);
|
|
||||||
// 调用委托并获取结果
|
|
||||||
|
|
||||||
|
|
||||||
FlipflopContext flipflopContext = await ((Func<object, object[], Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (flipflopContext != null)
|
|
||||||
{
|
|
||||||
if (flipflopContext.State == FfState.Cancel)
|
|
||||||
{
|
|
||||||
throw new Exception("取消此异步");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
FlowState = flipflopContext.State == FfState.Succeed;
|
|
||||||
result = flipflopContext.Data;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ExecuteStack(DynamicContext context)
|
|
||||||
{
|
|
||||||
await Task.Run(async () =>
|
|
||||||
{
|
|
||||||
await ExecuteStackTmp(context);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task ExecuteStackTmp(DynamicContext context)
|
|
||||||
{
|
|
||||||
var cts = context.ServiceContainer.Get<CancellationTokenSource>();
|
|
||||||
|
|
||||||
Stack<NodeBase> stack =[];
|
|
||||||
stack.Push(this);
|
|
||||||
|
|
||||||
while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环
|
|
||||||
{
|
|
||||||
// 从栈中弹出一个节点作为当前节点进行处理
|
|
||||||
var currentNode = stack.Pop();
|
|
||||||
|
|
||||||
if (currentNode.MethodDetails != null)
|
|
||||||
{
|
|
||||||
currentNode.MethodDetails.ActingInstance ??= context.ServiceContainer.Get(MethodDetails.ActingInstanceType);
|
|
||||||
} // 设置方法执行的对象
|
|
||||||
|
|
||||||
// 获取上游分支,首先执行一次
|
|
||||||
var upstreamNodes = currentNode.UpstreamBranch;
|
|
||||||
for (int i = upstreamNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
upstreamNodes[i].PreviousNode = currentNode;
|
|
||||||
await upstreamNodes[i].ExecuteStack(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == DynamicNodeType.Flipflop)
|
|
||||||
{
|
|
||||||
currentNode.FlowData = await currentNode.ExecuteAsync(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentNode.FlowData = currentNode.Execute(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var nextNodes = currentNode.FlowState ? currentNode.SucceedBranch
|
|
||||||
: currentNode.FailBranch;
|
|
||||||
|
|
||||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
|
||||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
nextNodes[i].PreviousNode = currentNode;
|
|
||||||
stack.Push(nextNodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public object[]? GetParameters(DynamicContext context, MethodDetails md)
|
|
||||||
{
|
|
||||||
// 用正确的大小初始化参数数组
|
|
||||||
var types = md.ExplicitDatas.Select(it => it.DataType).ToArray();
|
|
||||||
if (types.Length == 0)
|
|
||||||
{
|
|
||||||
return [md.ActingInstance];
|
|
||||||
}
|
|
||||||
|
|
||||||
object[]? parameters = new object[types.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < types.Length; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
var mdEd = md.ExplicitDatas[i];
|
|
||||||
Type type = mdEd.DataType;
|
|
||||||
|
|
||||||
var f1 = PreviousNode?.FlowData?.GetType();
|
|
||||||
var f2 = mdEd.DataType;
|
|
||||||
if (type == typeof(DynamicContext))
|
|
||||||
{
|
|
||||||
parameters[i] = context;
|
|
||||||
}
|
|
||||||
else if (type == typeof(MethodDetails))
|
|
||||||
{
|
|
||||||
parameters[i] = md;
|
|
||||||
}
|
|
||||||
else if (type == typeof(NodeBase))
|
|
||||||
{
|
|
||||||
parameters[i] = this;
|
|
||||||
}
|
|
||||||
else if (mdEd.IsExplicitData) // 显式参数
|
|
||||||
{
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(string))
|
|
||||||
{
|
|
||||||
parameters[i] = mdEd.DataValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(bool))
|
|
||||||
{
|
|
||||||
parameters[i] = bool.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(int))
|
|
||||||
{
|
|
||||||
parameters[i] = int.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(double))
|
|
||||||
{
|
|
||||||
parameters[i] = double.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = "";
|
|
||||||
|
|
||||||
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if ((f1 != null && f2 != null) && f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName))
|
|
||||||
{
|
|
||||||
parameters[i] = PreviousNode?.FlowData;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
var tmpParameter = PreviousNode?.FlowData?.ToString();
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, tmpParameter);
|
|
||||||
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(string))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = tmpParameter;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(bool))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = bool.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(int))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = int.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(double))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = double.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tmpParameter != null && mdEd.DataType!= null)
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = ConvertValue(tmpParameter, mdEd.DataType);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private dynamic? ConvertValue(string value, Type targetType)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject(value, targetType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JsonReaderException ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException ex)
|
|
||||||
{
|
|
||||||
// 如果无法转为对应的JSON对象
|
|
||||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
|
||||||
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
|
||||||
var typeInfo = ex.Message[startIndex..endIndex]; // 提取出错类型信息,该怎么传出去?
|
|
||||||
Console.WriteLine("无法转为对应的JSON对象:"+typeInfo);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch // (Exception ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 完整的ExecuteAsync调用方法(不要删除)
|
|
||||||
//public virtual async Task<object?> ExecuteAsync(DynamicContext context)
|
|
||||||
//{
|
|
||||||
// MethodDetails md = MethodDetails;
|
|
||||||
// object? result = null;
|
|
||||||
// if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
// {
|
|
||||||
// if (md.ExplicitDatas.Length == 0)
|
|
||||||
// {
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// else if (md.ReturnType == typeof(Task<FlipflopContext>))
|
|
||||||
// {
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// FlipflopContext flipflopContext = await ((Func<object, Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance);
|
|
||||||
|
|
||||||
// if (flipflopContext != null)
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Cancel)
|
|
||||||
// {
|
|
||||||
// throw new Exception("this async task is cancel.");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Succeed)
|
|
||||||
// {
|
|
||||||
// CurrentState = true;
|
|
||||||
// result = flipflopContext.Data;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// CurrentState = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// object?[]? parameters = GetParameters(context, MethodDetails);
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// else if (md.ReturnType == typeof(Task<FlipflopContext>))
|
|
||||||
// {
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// FlipflopContext flipflopContext = await ((Func<object, object[], Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
|
||||||
|
|
||||||
// if (flipflopContext != null)
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Cancel)
|
|
||||||
// {
|
|
||||||
// throw new Exception("取消此异步");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// CurrentState = flipflopContext.State == FfState.Succeed;
|
|
||||||
// result = flipflopContext.Data;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// context.SetFlowData(result);
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* while (stack.Count > 0) // 循环中直到栈为空才会退出
|
|
||||||
{
|
|
||||||
// 从栈中弹出一个节点作为当前节点进行处理
|
|
||||||
var currentNode = stack.Pop();
|
|
||||||
|
|
||||||
if(currentNode is CompositeActionNode || currentNode is CompositeConditionNode)
|
|
||||||
{
|
|
||||||
currentNode.currentState = true;
|
|
||||||
}
|
|
||||||
else if (currentNode is CompositeConditionNode)
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
currentNode.Execute(context);
|
|
||||||
// 根据当前节点的执行结果选择下一节点集合
|
|
||||||
// 如果 currentState 为真,选择 TrueBranchNextNodes;否则选择 FalseBranchNextNodes
|
|
||||||
var nextNodes = currentNode.currentState ? currentNode.TrueBranchNextNodes
|
|
||||||
: currentNode.FalseBranchNextNodes;
|
|
||||||
|
|
||||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
|
||||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
stack.Push(nextNodes[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
}*/
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 单动作节点(用于动作控件)
|
|
||||||
/// </summary>
|
|
||||||
public class SingleActionNode : NodeBase
|
|
||||||
{
|
|
||||||
//public override void Execute(DynamicContext context)
|
|
||||||
//{
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// Execute(context, base.MethodDetails);
|
|
||||||
// CurrentState = true;
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// Debug.Write(ex.Message);
|
|
||||||
// CurrentState = false;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public void Execute(DynamicContext context, MethodDetails md)
|
|
||||||
//{
|
|
||||||
// if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
// {
|
|
||||||
|
|
||||||
// object? result = null;
|
|
||||||
|
|
||||||
// if (md.ExplicitDatas.Length == 0)
|
|
||||||
// {
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// object?[]? parameters = GetParameters(context, MethodDetails);
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// // 根据 ExplicitDatas.Length 判断委托类型
|
|
||||||
// //var action = (Action<object, object[]>)del;
|
|
||||||
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// // action.Invoke(MethodDetails.ActingInstance, parameters);
|
|
||||||
|
|
||||||
// //parameters = [md.ActingInstance, "", 123, ""];
|
|
||||||
|
|
||||||
// context.SetFlowData(result);
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
using Serein.Flow.SerinExpression;
|
|
||||||
|
|
||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 条件节点(用于条件控件)
|
|
||||||
/// </summary>
|
|
||||||
public class SingleConditionNode : NodeBase
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为自定义参数
|
|
||||||
/// </summary>
|
|
||||||
public bool IsCustomData { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 自定义参数值
|
|
||||||
/// </summary>
|
|
||||||
public object? CustomData { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 条件表达式
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string Expression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override object? Execute(DynamicContext context)
|
|
||||||
{
|
|
||||||
// 接收上一节点参数or自定义参数内容
|
|
||||||
object? result;
|
|
||||||
if (IsCustomData)
|
|
||||||
{
|
|
||||||
result = CustomData;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = PreviousNode?.FlowData;
|
|
||||||
}
|
|
||||||
FlowState = SerinConditionParser.To(result, Expression);
|
|
||||||
Console.WriteLine($"{result} {Expression} -> " + FlowState);
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
//public override void Execute(DynamicContext context)
|
|
||||||
//{
|
|
||||||
// CurrentState = Judge(context, base.MethodDetails);
|
|
||||||
//}
|
|
||||||
|
|
||||||
//private bool Judge(DynamicContext context, MethodDetails md)
|
|
||||||
//{
|
|
||||||
// try
|
|
||||||
// {
|
|
||||||
// if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
// {
|
|
||||||
// object[] parameters = GetParameters(context, md);
|
|
||||||
// var temp = del.DynamicInvoke(parameters);
|
|
||||||
// //context.GetData(GetDyPreviousKey());
|
|
||||||
// return (bool)temp;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// catch (Exception ex)
|
|
||||||
// {
|
|
||||||
// Debug.Write(ex.Message);
|
|
||||||
// }
|
|
||||||
// return false;
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,35 +0,0 @@
|
|||||||
using Serein.Flow.SerinExpression;
|
|
||||||
|
|
||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Expression Operation - 表达式操作
|
|
||||||
/// </summary>
|
|
||||||
public class SingleExpOpNode : NodeBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式
|
|
||||||
/// </summary>
|
|
||||||
public string Expression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override object? Execute(DynamicContext context)
|
|
||||||
{
|
|
||||||
var data = PreviousNode?.FlowData;
|
|
||||||
|
|
||||||
var newData = SerinExpressionEvaluator.Evaluate(Expression, data, out bool isChange);
|
|
||||||
|
|
||||||
FlowState = true;
|
|
||||||
Console.WriteLine(newData);
|
|
||||||
if (isChange)
|
|
||||||
{
|
|
||||||
return newData;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return PreviousNode?.FlowData;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
namespace Serein.Flow.NodeModel
|
|
||||||
{
|
|
||||||
|
|
||||||
public class SingleFlipflopNode : NodeBase
|
|
||||||
{
|
|
||||||
public override object Execute(DynamicContext context)
|
|
||||||
{
|
|
||||||
throw new NotImplementedException("无法以非await/async的形式调用触发器");
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Serein.Flow.SerinExpression
|
|
||||||
{
|
|
||||||
|
|
||||||
public abstract class ConditionResolver
|
|
||||||
{
|
|
||||||
public abstract bool Evaluate(object obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PassConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Pass => true,
|
|
||||||
Operator.NotPass => false,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
Pass,
|
|
||||||
NotPass,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 不进行任何操作
|
|
||||||
/// </summary>
|
|
||||||
Node,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于
|
|
||||||
/// </summary>
|
|
||||||
LessThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 等于
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于或等于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于或等于
|
|
||||||
/// </summary>
|
|
||||||
LessThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 在两者之间
|
|
||||||
/// </summary>
|
|
||||||
InRange,
|
|
||||||
/// <summary>
|
|
||||||
/// 不在两者之间
|
|
||||||
/// </summary>
|
|
||||||
OutOfRange
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
public T RangeStart { get; set; }
|
|
||||||
public T RangeEnd { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is T typedObj)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(typedObj);
|
|
||||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
|
||||||
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
|
||||||
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
|
||||||
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
|
||||||
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
|
||||||
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
|
||||||
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
|
||||||
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.GreaterThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) > 0;
|
|
||||||
case Operator.LessThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) < 0;
|
|
||||||
case Operator.Equal:
|
|
||||||
return evaluatedValue.CompareTo(Value) == 0;
|
|
||||||
case Operator.GreaterThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) >= 0;
|
|
||||||
case Operator.LessThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) <= 0;
|
|
||||||
case Operator.InRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
|
||||||
case Operator.OutOfRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoolConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 是
|
|
||||||
/// </summary>
|
|
||||||
Is
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public bool Value { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj is bool boolObj)
|
|
||||||
{
|
|
||||||
return boolObj == Value;
|
|
||||||
/*switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Is:
|
|
||||||
return boolObj == Value;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 出现过
|
|
||||||
/// </summary>
|
|
||||||
Contains,
|
|
||||||
/// <summary>
|
|
||||||
/// 没有出现过
|
|
||||||
/// </summary>
|
|
||||||
DoesNotContain,
|
|
||||||
/// <summary>
|
|
||||||
/// 相等
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 不相等
|
|
||||||
/// </summary>
|
|
||||||
NotEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 起始字符串等于
|
|
||||||
/// </summary>
|
|
||||||
StartsWith,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束字符串等于
|
|
||||||
/// </summary>
|
|
||||||
EndsWith
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is string strObj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Contains => strObj.Contains(Value),
|
|
||||||
Operator.DoesNotContain => !strObj.Contains(Value),
|
|
||||||
Operator.Equal => strObj == Value,
|
|
||||||
Operator.NotEqual => strObj != Value,
|
|
||||||
Operator.StartsWith => strObj.StartsWith(Value),
|
|
||||||
Operator.EndsWith => strObj.EndsWith(Value),
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Contains:
|
|
||||||
return strObj.Contains(Value);
|
|
||||||
case Operator.DoesNotContain:
|
|
||||||
return !strObj.Contains(Value);
|
|
||||||
case Operator.Equal:
|
|
||||||
return strObj == Value;
|
|
||||||
case Operator.NotEqual:
|
|
||||||
return strObj != Value;
|
|
||||||
case Operator.StartsWith:
|
|
||||||
return strObj.StartsWith(Value);
|
|
||||||
case Operator.EndsWith:
|
|
||||||
return strObj.EndsWith(Value);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
//public string MemberPath { get; set; }
|
|
||||||
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
|
||||||
public object? TargetObj { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object? obj)
|
|
||||||
{
|
|
||||||
//object? memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (TargetObj is T typedObj)
|
|
||||||
{
|
|
||||||
return new ValueTypeConditionResolver<T>
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value,
|
|
||||||
ArithmeticExpression = ArithmeticExpression,
|
|
||||||
}.Evaluate(typedObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//private object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
//{
|
|
||||||
// string[] members = memberPath[1..].Split('.');
|
|
||||||
// foreach (var member in members)
|
|
||||||
// {
|
|
||||||
// if (obj == null) return null;
|
|
||||||
// Type type = obj.GetType();
|
|
||||||
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
// FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// obj = propertyInfo.GetValue(obj);
|
|
||||||
// else if (fieldInfo != null)
|
|
||||||
// obj = fieldInfo.GetValue(obj);
|
|
||||||
// else
|
|
||||||
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MemberStringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
|
|
||||||
public string MemberPath { get; set; }
|
|
||||||
|
|
||||||
public StringConditionResolver.Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
object memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (memberValue is string strObj)
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value
|
|
||||||
}.Evaluate(strObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.Flow.SerinExpression;
|
|
||||||
|
|
||||||
public class SerinConditionParser
|
|
||||||
{
|
|
||||||
public static bool To<T>(T data, string expression)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
return ConditionParse(data, expression).Evaluate(data);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConditionResolver ConditionParse(object data, string expression)
|
|
||||||
{
|
|
||||||
if (expression.StartsWith('.') /*&& expression.Contains('<') && expression.Contains('>')*/)
|
|
||||||
{
|
|
||||||
return ParseObjectExpression(data, expression);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ParseSimpleExpression(data, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ContainsArithmeticOperators(string expression)
|
|
||||||
{
|
|
||||||
return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
private static object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
if (obj == null) return null;
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
int typeStartIndex = expression.IndexOf('<');
|
|
||||||
int typeEndIndex = expression.IndexOf('>');
|
|
||||||
|
|
||||||
string memberPath;
|
|
||||||
Type type;
|
|
||||||
object? targetObj;
|
|
||||||
if ((typeStartIndex + typeStartIndex) == -2)
|
|
||||||
{
|
|
||||||
memberPath = operatorStr;
|
|
||||||
targetObj = GetMemberValue(data, operatorStr);
|
|
||||||
|
|
||||||
type = targetObj.GetType();
|
|
||||||
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (typeStartIndex >= typeEndIndex)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("无效的表达式格式");
|
|
||||||
}
|
|
||||||
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
|
||||||
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
|
||||||
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
|
||||||
if (parts.Length == 3)
|
|
||||||
{
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operatorStr = parts[0].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(1));
|
|
||||||
}
|
|
||||||
targetObj = GetMemberValue(data, memberPath);
|
|
||||||
|
|
||||||
Type? tempType = typeStr switch
|
|
||||||
{
|
|
||||||
"int" => typeof(int),
|
|
||||||
"double" => typeof(double),
|
|
||||||
"bool" => typeof(bool),
|
|
||||||
"string" => typeof(string),
|
|
||||||
_ => Type.GetType(typeStr)
|
|
||||||
};
|
|
||||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<int>
|
|
||||||
{
|
|
||||||
TargetObj = targetObj,
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
Op = ParseValueTypeOperator<int>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<double>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
return new MemberConditionResolver<bool>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new MemberStringConditionResolver
|
|
||||||
{
|
|
||||||
MemberPath = memberPath,
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
if ("pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.Pass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ("not pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if ("!pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
|
|
||||||
if (parts.Length < 2)
|
|
||||||
throw new ArgumentException("无效的表达式格式。");
|
|
||||||
|
|
||||||
//string typeStr = parts[0];
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
Type type = data.GetType();//Type.GetType(typeStr);
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
|
||||||
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
|
||||||
{
|
|
||||||
var temp = valueStr.Split('-');
|
|
||||||
if (temp.Length < 2)
|
|
||||||
throw new ArgumentException($"范围无效:{valueStr}。");
|
|
||||||
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
|
|
||||||
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
RangeStart = rangeStart,
|
|
||||||
RangeEnd = rangeEnd,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<double>
|
|
||||||
{
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
bool value = bool.Parse(valueStr);
|
|
||||||
return new BoolConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseBoolOperator(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
|
||||||
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
|
||||||
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
|
|
||||||
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"is" => BoolConditionResolver.Operator.Is,
|
|
||||||
"==" => BoolConditionResolver.Operator.Is,
|
|
||||||
"equals" => BoolConditionResolver.Operator.Is,
|
|
||||||
//"isFalse" => BoolConditionNode.Operator.IsFalse,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"c" => StringConditionResolver.Operator.Contains,
|
|
||||||
"nc" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"sw" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"ew" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
|
|
||||||
"contains" => StringConditionResolver.Operator.Contains,
|
|
||||||
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"equals" => StringConditionResolver.Operator.Equal,
|
|
||||||
"==" => StringConditionResolver.Operator.Equal,
|
|
||||||
"notEquals" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"!=" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"startsWith" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"endsWith" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static Serein.Flow.NodeModel.SingleExpOpNode;
|
|
||||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
||||||
|
|
||||||
namespace Serein.Flow.SerinExpression
|
|
||||||
{
|
|
||||||
public class SerinArithmeticExpressionEvaluator
|
|
||||||
{
|
|
||||||
private static readonly DataTable table = new DataTable();
|
|
||||||
|
|
||||||
public static double Evaluate(string expression, double inputValue)
|
|
||||||
{
|
|
||||||
// 替换占位符@为输入值
|
|
||||||
expression = expression.Replace("@", inputValue.ToString());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 使用 DataTable.Compute 方法计算表达式
|
|
||||||
var result = table.Compute(expression, string.Empty);
|
|
||||||
return Convert.ToDouble(result);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid arithmetic expression.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerinExpressionEvaluator
|
|
||||||
{
|
|
||||||
public static object Evaluate(string expression, object targetObJ,out bool IsChange)
|
|
||||||
{
|
|
||||||
var parts = expression.Split([' '], 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid expression format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var operation = parts[0].ToLower();
|
|
||||||
var operand = parts[1][0] == '.' ? parts[1][1..]: parts[1];
|
|
||||||
|
|
||||||
var result = operation switch
|
|
||||||
{
|
|
||||||
"@num" => ComputedNumber(targetObJ, operand),
|
|
||||||
"@call" => InvokeMethod(targetObJ, operand),
|
|
||||||
"@get" => GetMember(targetObJ, operand),
|
|
||||||
"@set" => SetMember(targetObJ, operand),
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
IsChange = operation switch
|
|
||||||
{
|
|
||||||
"@num" => true,
|
|
||||||
"@call" => true,
|
|
||||||
"@get" => true,
|
|
||||||
"@set" => false,
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly char[] separator = ['(', ')'];
|
|
||||||
private static readonly char[] separatorArray = [','];
|
|
||||||
|
|
||||||
private static object InvokeMethod(object target, string methodCall)
|
|
||||||
{
|
|
||||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (methodParts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid method call format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodName = methodParts[0];
|
|
||||||
var parameterList = methodParts[1];
|
|
||||||
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(p => p.Trim())
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var method = target.GetType().GetMethod(methodName);
|
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Method {methodName} not found on target.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameterValues = method.GetParameters()
|
|
||||||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
return method.Invoke(target, parameterValues);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object GetMember(object target, string memberPath)
|
|
||||||
{
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (target == null) return null;
|
|
||||||
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return target;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object SetMember(object target, string assignment)
|
|
||||||
{
|
|
||||||
var parts = assignment.Split(new[] { '=' }, 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid assignment format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var memberPath = parts[0].Trim();
|
|
||||||
var value = parts[1].Trim();
|
|
||||||
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
for (int i = 0; i < members.Length - 1; i++)
|
|
||||||
{
|
|
||||||
var member = members[i];
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastMember = members.Last();
|
|
||||||
|
|
||||||
var lastProperty = target.GetType().GetProperty(lastMember);
|
|
||||||
|
|
||||||
if (lastProperty != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
|
||||||
lastProperty.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lastField = target.GetType().GetField(lastMember);
|
|
||||||
if (lastField != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
|
||||||
lastField.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double ComputedNumber(object value,string expression)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(value);
|
|
||||||
if (!string.IsNullOrEmpty(expression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return numericValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,192 +0,0 @@
|
|||||||
using Serein.Library.IOC;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.Flow.Tool;
|
|
||||||
|
|
||||||
|
|
||||||
public static class DelegateCache
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 委托缓存全局字典
|
|
||||||
/// </summary>
|
|
||||||
public static ConcurrentDictionary<string, Delegate> GlobalDicDelegates { get; } = new ConcurrentDictionary<string, Delegate>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class DelegateGenerator
|
|
||||||
{
|
|
||||||
// 缓存的实例对象(键:类型名称)
|
|
||||||
public static ConcurrentDictionary<string, object> DynamicInstanceToType { get; } = new ConcurrentDictionary<string, object>();
|
|
||||||
// 缓存的实例对象 (键:生成的方法名称)
|
|
||||||
// public static ConcurrentDictionary<string, object> DynamicInstance { get; } = new ConcurrentDictionary<string, object>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 生成方法信息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="serviceContainer"></param>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static ConcurrentDictionary<string, MethodDetails> GenerateMethodDetails(IServiceContainer serviceContainer, Type type)
|
|
||||||
{
|
|
||||||
var methodDetailsDictionary = new ConcurrentDictionary<string, MethodDetails>();
|
|
||||||
var assemblyName = type.Assembly.GetName().Name;
|
|
||||||
var methods = GetMethodsToProcess(type);
|
|
||||||
|
|
||||||
foreach (var method in methods)
|
|
||||||
{
|
|
||||||
|
|
||||||
var methodDetails = CreateMethodDetails(serviceContainer, type, method, assemblyName);
|
|
||||||
|
|
||||||
methodDetailsDictionary.TryAdd(methodDetails.MethodName, methodDetails);
|
|
||||||
}
|
|
||||||
|
|
||||||
return methodDetailsDictionary;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取处理方法
|
|
||||||
/// </summary>
|
|
||||||
private static IEnumerable<MethodInfo> GetMethodsToProcess(Type type)
|
|
||||||
{
|
|
||||||
return type.GetMethods(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic)
|
|
||||||
.Where(m => m.GetCustomAttribute<MethodDetailAttribute>()?.Scan == true);
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 创建方法信息
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static MethodDetails CreateMethodDetails(IServiceContainer serviceContainer, Type type, MethodInfo method, string assemblyName)
|
|
||||||
{
|
|
||||||
var methodName = method.Name;
|
|
||||||
var attribute = method.GetCustomAttribute<MethodDetailAttribute>();
|
|
||||||
|
|
||||||
var explicitDataOfParameters = GetExplicitDataOfParameters(method.GetParameters());
|
|
||||||
// 生成委托
|
|
||||||
var methodDelegate = GenerateMethodDelegate(type, // 方法所在的对象类型
|
|
||||||
method, // 方法信息
|
|
||||||
method.GetParameters(),// 方法参数
|
|
||||||
method.ReturnType);// 返回值
|
|
||||||
|
|
||||||
|
|
||||||
var dllTypeName = $"{assemblyName}.{type.Name}";
|
|
||||||
serviceContainer.Register(type);
|
|
||||||
object instance = serviceContainer.GetOrCreateServiceInstance(type);
|
|
||||||
var dllTypeMethodName = $"{assemblyName}.{type.Name}.{method.Name}";
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return new MethodDetails
|
|
||||||
{
|
|
||||||
ActingInstanceType = type,
|
|
||||||
ActingInstance = instance,
|
|
||||||
MethodName = dllTypeMethodName,
|
|
||||||
MethodDelegate = methodDelegate,
|
|
||||||
MethodDynamicType = attribute.MethodDynamicType,
|
|
||||||
MethodLockName = attribute.LockName,
|
|
||||||
MethodTips = attribute.MethodTips,
|
|
||||||
ExplicitDatas = explicitDataOfParameters,
|
|
||||||
ReturnType = method.ReturnType,
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ExplicitData[] GetExplicitDataOfParameters(ParameterInfo[] parameters)
|
|
||||||
{
|
|
||||||
|
|
||||||
return parameters.Select((it, index) =>
|
|
||||||
{
|
|
||||||
//Console.WriteLine($"{it.Name}-{it.HasDefaultValue}-{it.DefaultValue}");
|
|
||||||
string explicitTypeName = GetExplicitTypeName(it.ParameterType);
|
|
||||||
var items = GetExplicitItems(it.ParameterType, explicitTypeName);
|
|
||||||
if ("Bool".Equals(explicitTypeName)) explicitTypeName = "Select"; // 布尔值 转为 可选类型
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return new ExplicitData
|
|
||||||
{
|
|
||||||
IsExplicitData = it.GetCustomAttribute(typeof(ExplicitAttribute)) is ExplicitAttribute,
|
|
||||||
Index = index,
|
|
||||||
ExplicitType = it.ParameterType,
|
|
||||||
ExplicitTypeName = explicitTypeName,
|
|
||||||
DataType = it.ParameterType,
|
|
||||||
ParameterName = it.Name,
|
|
||||||
DataValue = it.HasDefaultValue ? it.DefaultValue.ToString() : "",
|
|
||||||
Items = items.ToArray(),
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}).ToArray();
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetExplicitTypeName(Type type)
|
|
||||||
{
|
|
||||||
return type switch
|
|
||||||
{
|
|
||||||
Type t when t.IsEnum => "Select",
|
|
||||||
Type t when t == typeof(bool) => "Bool",
|
|
||||||
Type t when t == typeof(string) => "Value",
|
|
||||||
Type t when t == typeof(int) => "Value",
|
|
||||||
Type t when t == typeof(double) => "Value",
|
|
||||||
_ => "Value"
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static IEnumerable<string> GetExplicitItems(Type type, string explicitTypeName)
|
|
||||||
{
|
|
||||||
return explicitTypeName switch
|
|
||||||
{
|
|
||||||
"Select" => Enum.GetNames(type),
|
|
||||||
"Bool" => ["True", "False"],
|
|
||||||
_ => []
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Delegate GenerateMethodDelegate(Type type, MethodInfo methodInfo, ParameterInfo[] parameters, Type returnType)
|
|
||||||
{
|
|
||||||
var parameterTypes = parameters.Select(p => p.ParameterType).ToArray();
|
|
||||||
var parameterCount = parameters.Length;
|
|
||||||
|
|
||||||
if (returnType == typeof(void))
|
|
||||||
{
|
|
||||||
if (parameterCount == 0)
|
|
||||||
{
|
|
||||||
// 无返回值,无参数
|
|
||||||
return ExpressionHelper.MethodCaller(type, methodInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 无返回值,有参数
|
|
||||||
return ExpressionHelper.MethodCaller(type, methodInfo, parameterTypes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (returnType == typeof(Task<FlipflopContext>)) // 触发器
|
|
||||||
{
|
|
||||||
if (parameterCount == 0)
|
|
||||||
{
|
|
||||||
// 有返回值,无参数
|
|
||||||
return ExpressionHelper.MethodCallerAsync(type, methodInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 有返回值,有参数
|
|
||||||
return ExpressionHelper.MethodCallerAsync(type, methodInfo, parameterTypes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (parameterCount == 0)
|
|
||||||
{
|
|
||||||
// 有返回值,无参数
|
|
||||||
return ExpressionHelper.MethodCallerHaveResult(type, methodInfo);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 有返回值,有参数
|
|
||||||
return ExpressionHelper.MethodCallerHaveResult(type, methodInfo, parameterTypes);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
namespace Serein.Flow.Tool
|
|
||||||
{
|
|
||||||
|
|
||||||
#region 锁、tsk工具 (已注释)
|
|
||||||
/*public class LockManager
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, LockQueue> _locks = new ConcurrentDictionary<string, LockQueue>();
|
|
||||||
|
|
||||||
public void CreateLock(string name)
|
|
||||||
{
|
|
||||||
_locks.TryAdd(name, new LockQueue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!_locks.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockQueue = _locks[name];
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
lockQueue.Queue.Enqueue(tcs);
|
|
||||||
if (lockQueue.Queue.Count == 1)
|
|
||||||
{
|
|
||||||
tcs.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// 处理取消操作
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
cancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
if (lockQueue.Queue.Contains(tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReleaseLock(string name)
|
|
||||||
{
|
|
||||||
if (!_locks.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockQueue = _locks[name];
|
|
||||||
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
if (lockQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
lockQueue.Queue.Dequeue();
|
|
||||||
|
|
||||||
if (lockQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
var next = lockQueue.Queue.Peek();
|
|
||||||
next.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LockQueue
|
|
||||||
{
|
|
||||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface ITaskResult
|
|
||||||
{
|
|
||||||
object Result { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TaskResult<T> : ITaskResult
|
|
||||||
{
|
|
||||||
public TaskResult(T result)
|
|
||||||
{
|
|
||||||
Result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Result { get; }
|
|
||||||
|
|
||||||
object ITaskResult.Result => Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DynamicTasks
|
|
||||||
{
|
|
||||||
private static readonly ConcurrentDictionary<string, Task<ITaskResult>> TaskGuidPairs = new();
|
|
||||||
public static Task<ITaskResult> GetTask(string Guid)
|
|
||||||
{
|
|
||||||
TaskGuidPairs.TryGetValue(Guid, out Task<ITaskResult> task);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool AddTask<T>(string Guid, T result)
|
|
||||||
{
|
|
||||||
var task = Task.FromResult<ITaskResult>(new TaskResult<T>(result));
|
|
||||||
|
|
||||||
return TaskGuidPairs.TryAdd(Guid, task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class TaskNodeManager
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, TaskQueue> _taskQueues = new ConcurrentDictionary<string, TaskQueue>();
|
|
||||||
|
|
||||||
public void CreateTaskNode(string name)
|
|
||||||
{
|
|
||||||
_taskQueues.TryAdd(name, new TaskQueue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!_taskQueues.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskQueue = _taskQueues[name];
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
taskQueue.Queue.Enqueue(tcs);
|
|
||||||
if (taskQueue.Queue.Count == 1)
|
|
||||||
{
|
|
||||||
tcs.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// 处理取消操作
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
cancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
if (taskQueue.Queue.Contains(tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CompleteTaskNode(string name)
|
|
||||||
{
|
|
||||||
if (!_taskQueues.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskQueue = _taskQueues[name];
|
|
||||||
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
if (taskQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
taskQueue.Queue.Dequeue();
|
|
||||||
|
|
||||||
if (taskQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
var next = taskQueue.Queue.Peek();
|
|
||||||
next.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TaskQueue
|
|
||||||
{
|
|
||||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,740 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Linq.Expressions;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.Flow.Tool
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 对于实例创建的表达式树反射
|
|
||||||
/// </summary>
|
|
||||||
public static class ExpressionHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 缓存表达式树反射方法
|
|
||||||
/// </summary>
|
|
||||||
private static ConcurrentDictionary<string, Delegate> Cache { get; } = new ConcurrentDictionary<string, Delegate>();
|
|
||||||
|
|
||||||
public static List<string> GetCacheKey()
|
|
||||||
{
|
|
||||||
return [.. Cache.Keys];
|
|
||||||
}
|
|
||||||
|
|
||||||
#region 基于类型的表达式反射构建委托
|
|
||||||
|
|
||||||
#region 属性、字段的委托创建(表达式反射)
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态获取属性值
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate PropertyGetter(Type type, string propertyName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{propertyName}.Getter";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateGetterDelegate(type, propertyName));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 动态获取属性值
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateGetterDelegate(Type type, string propertyName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var property = Expression.Property(Expression.Convert(parameter, type), propertyName);
|
|
||||||
var lambda = Expression.Lambda(Expression.Convert(property, typeof(object)), parameter);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态设置属性值
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate PropertySetter(Type type, string propertyName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{propertyName}.Setter";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateSetterDelegate(type, propertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态设置属性值
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateSetterDelegate(Type type, string propertyName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var value = Expression.Parameter(typeof(object), "value");
|
|
||||||
var property = Expression.Property(Expression.Convert(parameter, type), propertyName);
|
|
||||||
var assign = Expression.Assign(property, Expression.Convert(value, property.Type));
|
|
||||||
var lambda = Expression.Lambda(assign, parameter, value);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态获取字段值
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate FieldGetter(Type type, string fieldName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{fieldName}.FieldGetter";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateFieldGetterDelegate(type, fieldName));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 动态获取字段值
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateFieldGetterDelegate(Type type, string fieldName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var field = Expression.Field(Expression.Convert(parameter, type), fieldName);
|
|
||||||
var lambda = Expression.Lambda(Expression.Convert(field, typeof(object)), parameter);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态设置字段值
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate FieldSetter(Type type, string fieldName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{fieldName}.FieldSetter";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateFieldSetterDelegate(type, fieldName));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 动态设置字段值
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateFieldSetterDelegate(Type type, string fieldName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var value = Expression.Parameter(typeof(object), "value");
|
|
||||||
var field = Expression.Field(Expression.Convert(parameter, type), fieldName);
|
|
||||||
var assign = Expression.Assign(field, Expression.Convert(value, field.Type));
|
|
||||||
var lambda = Expression.Lambda(assign, parameter, value);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建无参数,无返回值方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCaller(Type type, MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建无参数,无返回值方法
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegate(Type type, MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo);
|
|
||||||
var lambda = Expression.Lambda(methodCall, parameter);
|
|
||||||
// Action<object>
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建无参数,有返回值方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建无参数,有返回值方法
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegateHaveResult(Type type, MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo);
|
|
||||||
var lambda = Expression.Lambda(Expression.Convert(methodCall, typeof(object)), parameter);
|
|
||||||
// Func<object, object>
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建多个参数,无返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCaller(Type type, MethodInfo methodInfo, params Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCaller";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodInfo, parameterTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建多个参数,无返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegate(Type type, MethodInfo methodInfo, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
/* var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
|
|
||||||
var arguments = parameterTypes.Select((t, i) => Expression.Parameter(typeof(object), $"arg{i}")).ToArray();
|
|
||||||
|
|
||||||
var convertedArguments = arguments.Select((arg, i) => Expression.Convert(arg, parameterTypes[i])).ToArray();
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type),
|
|
||||||
methodInfo,
|
|
||||||
convertedArguments);
|
|
||||||
var lambda = Expression.Lambda(methodCall, new[] { parameter }.Concat(arguments));
|
|
||||||
var tmpAction = lambda.Compile();
|
|
||||||
|
|
||||||
// Action<object, object[]>
|
|
||||||
return lambda.Compile();*/
|
|
||||||
|
|
||||||
var instanceParam = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argsParam = Expression.Parameter(typeof(object[]), "args");
|
|
||||||
|
|
||||||
// 创建参数表达式
|
|
||||||
var convertedArgs = parameterTypes.Select((paramType, index) =>
|
|
||||||
Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType)
|
|
||||||
).ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
// 创建方法调用表达式
|
|
||||||
var methodCall = Expression.Call(
|
|
||||||
Expression.Convert(instanceParam, type),
|
|
||||||
methodInfo,
|
|
||||||
(Expression[])convertedArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建 lambda 表达式
|
|
||||||
var lambda = Expression.Lambda(
|
|
||||||
methodCall,
|
|
||||||
instanceParam,
|
|
||||||
argsParam
|
|
||||||
);
|
|
||||||
|
|
||||||
// Func<object, object[], object>
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建多个参数,有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCallerHaveResult(Type type, MethodInfo methodInfo, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerHaveResult";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodInfo, parameterTypes));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建多个参数,有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegateHaveResult(Type type, MethodInfo methodInfo, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
/*var instanceParam = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argsParam = Expression.Parameter(typeof(object[]), "args");
|
|
||||||
|
|
||||||
// 创建参数表达式
|
|
||||||
var convertedArgs = parameterTypes.Select((paramType, index) =>
|
|
||||||
Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType)
|
|
||||||
).ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
// 创建方法调用表达式
|
|
||||||
var methodCall = Expression.Call(
|
|
||||||
Expression.Convert(instanceParam, type),
|
|
||||||
methodInfo,
|
|
||||||
convertedArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建 lambda 表达式
|
|
||||||
var lambda = Expression.Lambda(
|
|
||||||
Expression.Convert(methodCall, typeof(object)),
|
|
||||||
instanceParam,
|
|
||||||
argsParam
|
|
||||||
);
|
|
||||||
|
|
||||||
// Func<object, object[], object>
|
|
||||||
return lambda.Compile();*/
|
|
||||||
|
|
||||||
var instanceParam = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argsParam = Expression.Parameter(typeof(object[]), "args");
|
|
||||||
|
|
||||||
// 创建参数表达式
|
|
||||||
var convertedArgs = parameterTypes.Select((paramType, index) =>
|
|
||||||
Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType)
|
|
||||||
).ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
// 创建方法调用表达式
|
|
||||||
var methodCall = Expression.Call(
|
|
||||||
Expression.Convert(instanceParam, type),
|
|
||||||
methodInfo,
|
|
||||||
convertedArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建 lambda 表达式
|
|
||||||
var lambda = Expression.Lambda<Func<object, object[], object>>(
|
|
||||||
Expression.Convert(methodCall, typeof(object)),
|
|
||||||
instanceParam,
|
|
||||||
argsParam
|
|
||||||
);
|
|
||||||
//var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCallerAsync(Type type, MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodInfo.Name}.MethodCallerAsync";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, methodInfo));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建无参数,有返回值(Task<object>)的方法(触发器)
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type), methodInfo);
|
|
||||||
var lambda = Expression.Lambda<Func<object, Task<object>>>(
|
|
||||||
Expression.Convert(methodCall, typeof(Task<object>)), parameter);
|
|
||||||
// Func<object, Task<object>>
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建多个参数,有返回值(Task-object)的方法(触发器)
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCallerAsync(Type type, MethodInfo method, params Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
|
|
||||||
string cacheKey = $"{type.FullName}.{method.Name}.MethodCallerAsync";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateAsync(type, method, parameterTypes));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建多个参数,有返回值(Task<object>)的方法(触发器)
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegateAsync(Type type, MethodInfo methodInfo, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
var instanceParam = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argsParam = Expression.Parameter(typeof(object[]), "args");
|
|
||||||
|
|
||||||
// 创建参数表达式
|
|
||||||
var convertedArgs = parameterTypes.Select((paramType, index) =>
|
|
||||||
Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)),paramType)
|
|
||||||
).ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
// 创建方法调用表达式
|
|
||||||
var methodCall = Expression.Call(
|
|
||||||
Expression.Convert(instanceParam, type),
|
|
||||||
methodInfo,
|
|
||||||
(Expression[])convertedArgs
|
|
||||||
);
|
|
||||||
|
|
||||||
// 创建 lambda 表达式
|
|
||||||
var lambda = Expression.Lambda<Func<object, object[], Task<FlipflopContext>>>(
|
|
||||||
Expression.Convert(methodCall, typeof(Task<FlipflopContext>)),
|
|
||||||
instanceParam,
|
|
||||||
argsParam
|
|
||||||
);
|
|
||||||
//var resule = task.DynamicInvoke((object)[Activator.CreateInstance(type), [new DynamicContext(null)]]);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 单参数
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建单参数,无返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCaller(Type type, string methodName, Type parameterType)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodName}.MethodCallerWithParam";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodName, parameterType));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建单参数,无返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegate(Type type, string methodName, Type parameterType)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argument = Expression.Parameter(typeof(object), "argument");
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type),
|
|
||||||
type.GetMethod(methodName, [parameterType])!,
|
|
||||||
Expression.Convert(argument, parameterType));
|
|
||||||
var lambda = Expression.Lambda(methodCall, parameter, argument);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建单参数,有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCallerWithResult(Type type, string methodName, Type parameterType, Type returnType)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodName}.MethodCallerWithResult";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateWithResult(type, methodName, parameterType, returnType));
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式树构建单参数,有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
private static Delegate CreateMethodCallerDelegateWithResult(Type type, string methodName, Type parameterType, Type returnType)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argument = Expression.Parameter(typeof(object), "argument");
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type),
|
|
||||||
type.GetMethod(methodName, [parameterType])!,
|
|
||||||
Expression.Convert(argument, parameterType));
|
|
||||||
var lambda = Expression.Lambda(Expression.Convert(methodCall, typeof(object)), parameter, argument);
|
|
||||||
|
|
||||||
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
#region 泛型表达式反射构建方法(已注释)
|
|
||||||
/*
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态获取属性值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TProperty"></typeparam>
|
|
||||||
/// <param name="propertyName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<T, TProperty> PropertyGetter<T, TProperty>(string propertyName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{propertyName}.Getter";
|
|
||||||
return (Func<T, TProperty>)Cache.GetOrAdd(cacheKey, _ => CreateGetterDelegate<T, TProperty>(propertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Func<T, TProperty> CreateGetterDelegate<T, TProperty>(string propertyName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var property = Expression.Property(parameter, propertyName);
|
|
||||||
var lambda = Expression.Lambda<Func<T, TProperty>>(property, parameter);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态设置属性值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TProperty"></typeparam>
|
|
||||||
/// <param name="propertyName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Action<T, TProperty> PropertySetter<T, TProperty>(string propertyName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{propertyName}.Setter";
|
|
||||||
return (Action<T, TProperty>)Cache.GetOrAdd(cacheKey, _ => CreateSetterDelegate<T, TProperty>(propertyName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<T, TProperty> CreateSetterDelegate<T, TProperty>(string propertyName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var value = Expression.Parameter(typeof(TProperty), "value");
|
|
||||||
var property = Expression.Property(parameter, propertyName);
|
|
||||||
var assign = Expression.Assign(property, value);
|
|
||||||
var lambda = Expression.Lambda<Action<T, TProperty>>(assign, parameter, value);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态获取字段值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TField"></typeparam>
|
|
||||||
/// <param name="fieldName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<T, TField> FieldGetter<T, TField>(string fieldName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{fieldName}.FieldGetter";
|
|
||||||
return (Func<T, TField>)Cache.GetOrAdd(cacheKey, _ => CreateFieldGetterDelegate<T, TField>(fieldName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Func<T, TField> CreateFieldGetterDelegate<T, TField>(string fieldName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var field = Expression.Field(parameter, fieldName);
|
|
||||||
var lambda = Expression.Lambda<Func<T, TField>>(field, parameter);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态设置字段值
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TField"></typeparam>
|
|
||||||
/// <param name="fieldName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Action<T, TField> FieldSetter<T, TField>(string fieldName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{fieldName}.FieldSetter";
|
|
||||||
return (Action<T, TField>)Cache.GetOrAdd(cacheKey, _ => CreateFieldSetterDelegate<T, TField>(fieldName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<T, TField> CreateFieldSetterDelegate<T, TField>(string fieldName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var value = Expression.Parameter(typeof(TField), "value");
|
|
||||||
var field = Expression.Field(parameter, fieldName);
|
|
||||||
var assign = Expression.Assign(field, value);
|
|
||||||
var lambda = Expression.Lambda<Action<T, TField>>(assign, parameter, value);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态调用无参数方法
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Action<T> MethodCaller<T>(string methodName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCaller";
|
|
||||||
return (Action<T>)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate<T>(methodName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<T> CreateMethodCallerDelegate<T>(string methodName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName));
|
|
||||||
var lambda = Expression.Lambda<Action<T>>(methodCall, parameter);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态调用无参有返回值方法
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TResult"></typeparam>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<T, TResult> MethodCallerHaveResul<T, TResult>(string methodName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCaller";
|
|
||||||
return (Func<T, TResult>)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult<T, TResult>(methodName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Func<T, TResult> CreateMethodCallerDelegateHaveResult<T, TResult>(string methodName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName));
|
|
||||||
var lambda = Expression.Lambda<Func<T, TResult>>(methodCall, parameter);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态调用单参数无返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TParam"></typeparam>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Action<T, TParam> MethodCaller<T, TParam>(string methodName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCallerWithParam";
|
|
||||||
return (Action<T, TParam>)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate<T, TParam>(methodName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<T, TParam> CreateMethodCallerDelegate<T, TParam>(string methodName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var argument = Expression.Parameter(typeof(TParam), "argument");
|
|
||||||
var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName), argument);
|
|
||||||
var lambda = Expression.Lambda<Action<T, TParam>>(methodCall, parameter, argument);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态调用单参数有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TParam"></typeparam>
|
|
||||||
/// <typeparam name="TResult"></typeparam>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<T, TParam, TResult> MethodCallerWithResult<T, TParam, TResult>(string methodName)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCallerWithResult";
|
|
||||||
return (Func<T, TParam, TResult>)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate<T, TParam, TResult>(methodName));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Func<T, TParam, TResult> CreateMethodCallerDelegate<T, TParam, TResult>(string methodName)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var argument = Expression.Parameter(typeof(TParam), "argument");
|
|
||||||
var methodCall = Expression.Call(parameter, typeof(T).GetMethod(methodName), argument);
|
|
||||||
var lambda = Expression.Lambda<Func<T, TParam, TResult>>(methodCall, parameter, argument);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态调用多参无返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <param name="parameterTypes"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Action<T, object[]> MethodCaller<T>(string methodName, params Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCaller";
|
|
||||||
return (Action<T, object[]>)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate<T>(methodName, parameterTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Action<T, object[]> CreateMethodCallerDelegate<T>(string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var arguments = parameterTypes.Select((type, index) =>
|
|
||||||
Expression.Parameter(typeof(object), $"arg{index}")
|
|
||||||
).ToList();
|
|
||||||
|
|
||||||
var convertedArguments = arguments.Select((arg, index) =>
|
|
||||||
Expression.Convert(arg, parameterTypes[index])
|
|
||||||
).ToList();
|
|
||||||
|
|
||||||
var methodInfo = typeof(T).GetMethod(methodName, parameterTypes);
|
|
||||||
|
|
||||||
if (methodInfo == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Method '{methodName}' not found in type '{typeof(T).FullName}' with given parameter types.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodCall = Expression.Call(parameter, methodInfo, convertedArguments);
|
|
||||||
var lambda = Expression.Lambda<Action<T, object[]>>(methodCall, new[] { parameter }.Concat(arguments));
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态调用多参有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <typeparam name="TResult"></typeparam>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <param name="parameterTypes"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Func<T, object[], TResult> MethodCallerHaveResult<T, TResult>(string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{typeof(T).FullName}.{methodName}.MethodCallerHaveResult";
|
|
||||||
return (Func<T, object[], TResult>)Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate<T, TResult>(methodName, parameterTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Func<T, object[], TResult> CreateMethodCallerDelegate<T, TResult>(string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
var instanceParam = Expression.Parameter(typeof(T), "instance");
|
|
||||||
var argsParam = Expression.Parameter(typeof(object[]), "args");
|
|
||||||
|
|
||||||
var convertedArgs = new Expression[parameterTypes.Length];
|
|
||||||
for (int i = 0; i < parameterTypes.Length; i++)
|
|
||||||
{
|
|
||||||
var index = Expression.Constant(i);
|
|
||||||
var argType = parameterTypes[i];
|
|
||||||
var arrayIndex = Expression.ArrayIndex(argsParam, index);
|
|
||||||
var convertedArg = Expression.Convert(arrayIndex, argType);
|
|
||||||
convertedArgs[i] = convertedArg;
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodInfo = typeof(T).GetMethod(methodName, parameterTypes);
|
|
||||||
|
|
||||||
if (methodInfo == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Method '{methodName}' not found in type '{typeof(T).FullName}' with given parameter types.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodCall = Expression.Call(instanceParam, methodInfo, convertedArgs);
|
|
||||||
var lambda = Expression.Lambda<Func<T, object[], TResult>>(methodCall, instanceParam, argsParam);
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
#region 暂时不删(已注释)
|
|
||||||
/* /// <summary>
|
|
||||||
/// 表达式树构建多个参数,有返回值的方法
|
|
||||||
/// </summary>
|
|
||||||
public static Delegate MethodCallerHaveResult(Type type, string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodName}.MethodCallerHaveResult";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodName, parameterTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Delegate CreateMethodCallerDelegateHaveResult(Type type, string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
var instanceParam = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var argsParam = Expression.Parameter(typeof(object[]), "args");
|
|
||||||
var convertedArgs = parameterTypes.Select((paramType, index) =>
|
|
||||||
Expression.Convert(Expression.ArrayIndex(argsParam, Expression.Constant(index)), paramType)
|
|
||||||
).ToArray();
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(instanceParam, type), type.GetMethod(methodName, parameterTypes), convertedArgs);
|
|
||||||
var lambda = Expression.Lambda(Expression.Convert(methodCall, typeof(object)), instanceParam, argsParam);
|
|
||||||
return lambda.Compile();
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/*/// <summary>
|
|
||||||
/// 表达式反射 构建 无返回值、无参数 的委托
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <param name="parameterTypes"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static Delegate MethodCaller(Type type, string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodName}.{string.Join(",", parameterTypes.Select(t => t.FullName))}.MethodCaller";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegate(type, methodName, parameterTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表达式反射 构建 无返回值、无参数 的委托
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="type"></param>
|
|
||||||
/// <param name="methodName"></param>
|
|
||||||
/// <param name="parameterTypes"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static Delegate CreateMethodCallerDelegate(Type type, string methodName, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var arguments = parameterTypes.Select((paramType, index) => Expression.Parameter(paramType, $"param{index}")).ToArray();
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type), type.GetMethod(methodName, parameterTypes), arguments);
|
|
||||||
|
|
||||||
var delegateType = Expression.GetActionType(new[] { typeof(object) }.Concat(parameterTypes).ToArray());
|
|
||||||
var lambda = Expression.Lambda(delegateType, methodCall, new[] { parameter }.Concat(arguments).ToArray());
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
/*public static Delegate MethodCallerHaveResult(Type type, string methodName, Type returnType, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
string cacheKey = $"{type.FullName}.{methodName}.{string.Join(",", parameterTypes.Select(t => t.FullName))}.MethodCallerHaveResult";
|
|
||||||
return Cache.GetOrAdd(cacheKey, _ => CreateMethodCallerDelegateHaveResult(type, methodName, returnType, parameterTypes));
|
|
||||||
}
|
|
||||||
|
|
||||||
private static Delegate CreateMethodCallerDelegateHaveResult(Type type, string methodName, Type returnType, Type[] parameterTypes)
|
|
||||||
{
|
|
||||||
var parameter = Expression.Parameter(typeof(object), "instance");
|
|
||||||
var arguments = parameterTypes.Select((paramType, index) => Expression.Parameter(paramType, $"param{index}")).ToArray();
|
|
||||||
var methodCall = Expression.Call(Expression.Convert(parameter, type), type.GetMethod(methodName, parameterTypes), arguments);
|
|
||||||
|
|
||||||
var delegateType = Expression.GetFuncType(new[] { typeof(object) }.Concat(parameterTypes).Concat(new[] { typeof(object) }).ToArray());
|
|
||||||
var lambda = Expression.Lambda(delegateType, Expression.Convert(methodCall, typeof(object)), new[] { parameter }.Concat(arguments).ToArray());
|
|
||||||
return lambda.Compile();
|
|
||||||
}
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,80 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
|
|
||||||
namespace Serein.Flow.Tool
|
|
||||||
{
|
|
||||||
public class TcsSignalException : Exception
|
|
||||||
{
|
|
||||||
public FfState FfState { get; set; }
|
|
||||||
public TcsSignalException(string? message) : base(message)
|
|
||||||
{
|
|
||||||
FfState = FfState.Cancel;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TcsSignal<TSignal> where TSignal : struct, Enum
|
|
||||||
{
|
|
||||||
|
|
||||||
public ConcurrentDictionary<TSignal, Stack<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
|
||||||
|
|
||||||
// public object tcsObj = new object();
|
|
||||||
|
|
||||||
public bool TriggerSignal<T>(TSignal signal, T state)
|
|
||||||
{
|
|
||||||
if (TcsEvent.TryRemove(signal, out var waitTcss))
|
|
||||||
{
|
|
||||||
while (waitTcss.Count > 0)
|
|
||||||
{
|
|
||||||
|
|
||||||
waitTcss.Pop().SetResult(state);
|
|
||||||
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
|
||||||
{
|
|
||||||
|
|
||||||
var tcs = new TaskCompletionSource<object>();
|
|
||||||
TcsEvent.GetOrAdd(signal, _ => new Stack<TaskCompletionSource<object>>()).Push(tcs);
|
|
||||||
return tcs;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
//public TaskCompletionSource<object> GetOrCreateTcs(TSignal signal)
|
|
||||||
//{
|
|
||||||
// lock (tcsObj)
|
|
||||||
// {
|
|
||||||
// var tcs = TcsEvent.GetOrAdd(signal, _ => new TaskCompletionSource<object>());
|
|
||||||
// if (tcs.Task.IsCompleted)
|
|
||||||
// {
|
|
||||||
// TcsEvent.TryRemove(signal, out _);
|
|
||||||
// tcs = new TaskCompletionSource<object>();
|
|
||||||
// TcsEvent[signal] = tcs;
|
|
||||||
// }
|
|
||||||
// return tcs;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
public void CancelTask()
|
|
||||||
{
|
|
||||||
lock(TcsEvent)
|
|
||||||
{
|
|
||||||
|
|
||||||
foreach (var tcss in TcsEvent.Values)
|
|
||||||
{
|
|
||||||
while (tcss.Count > 0)
|
|
||||||
{
|
|
||||||
tcss.Pop().SetException(new TcsSignalException("Task Cancel"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
TcsEvent.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,373 +0,0 @@
|
|||||||
using Serein.Library.Api;
|
|
||||||
using Serein.Library.Attributes;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.Library.Core.IOC
|
|
||||||
{
|
|
||||||
public class SereinIoc : ISereinIoc
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, object> _dependencies;
|
|
||||||
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
|
||||||
private readonly List<Type> _waitingForInstantiation;
|
|
||||||
|
|
||||||
public SereinIoc()
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies = new ConcurrentDictionary<string, object>
|
|
||||||
{
|
|
||||||
[typeof(ISereinIoc).FullName] = this
|
|
||||||
};
|
|
||||||
|
|
||||||
_typeMappings = new ConcurrentDictionary<string, Type>();
|
|
||||||
_waitingForInstantiation = [];
|
|
||||||
}
|
|
||||||
public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
|
||||||
{
|
|
||||||
Register(type);
|
|
||||||
object instance;
|
|
||||||
|
|
||||||
if (_dependencies.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
instance = _dependencies[type.FullName];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
instance = Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
|
|
||||||
_dependencies[type.FullName] = instance;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
public T CreateServiceInstance<T>(params object[] parameters)
|
|
||||||
{
|
|
||||||
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Reset()
|
|
||||||
{
|
|
||||||
foreach(var instancei in _dependencies.Values)
|
|
||||||
{
|
|
||||||
if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
|
||||||
{
|
|
||||||
disposable.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_dependencies.Clear();
|
|
||||||
_waitingForInstantiation.Clear();
|
|
||||||
//_typeMappings.Clear();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Register(Type type, params object[] parameters)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!_typeMappings.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
_typeMappings[type.FullName] = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public ISereinIoc Register<T>(params object[] parameters)
|
|
||||||
{
|
|
||||||
Register(typeof(T), parameters);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
|
||||||
where TImplementation : TService
|
|
||||||
{
|
|
||||||
_typeMappings[typeof(TService).FullName!] = typeof(TImplementation);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object GetOrInstantiate(Type type)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
|
||||||
{
|
|
||||||
Register(type);
|
|
||||||
|
|
||||||
value = Instantiate(type);
|
|
||||||
|
|
||||||
InjectDependencies(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public T GetOrInstantiate<T>()
|
|
||||||
{
|
|
||||||
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
|
||||||
{
|
|
||||||
Register<T>();
|
|
||||||
|
|
||||||
value = Instantiate(typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (T)value;
|
|
||||||
//throw new InvalidOperationException("目标类型未创建实例");
|
|
||||||
}
|
|
||||||
public ISereinIoc Build()
|
|
||||||
{
|
|
||||||
foreach (var type in _typeMappings.Values)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(!_dependencies.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var instance in _dependencies.Values)
|
|
||||||
{
|
|
||||||
|
|
||||||
InjectDependencies(instance); // 替换占位符
|
|
||||||
}
|
|
||||||
|
|
||||||
//var instance = Instantiate(item.Value);
|
|
||||||
|
|
||||||
TryInstantiateWaitingDependencies();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object? Instantiate(Type controllerType, params object[] parameters)
|
|
||||||
{
|
|
||||||
var instance = Activator.CreateInstance(controllerType, parameters);
|
|
||||||
if(instance != null)
|
|
||||||
{
|
|
||||||
InjectDependencies(instance);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InjectDependencies(object instance)
|
|
||||||
{
|
|
||||||
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
||||||
.Where(p => p.CanWrite && p.GetCustomAttribute<AutoInjectionAttribute>() != null);
|
|
||||||
|
|
||||||
foreach (var property in properties)
|
|
||||||
{
|
|
||||||
var propertyType = property.PropertyType;
|
|
||||||
|
|
||||||
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
|
||||||
{
|
|
||||||
property.SetValue(instance, dependencyInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryInstantiateWaitingDependencies()
|
|
||||||
{
|
|
||||||
foreach (var waitingType in _waitingForInstantiation.ToList())
|
|
||||||
{
|
|
||||||
if (_typeMappings.TryGetValue(waitingType.FullName!, out var implementationType))
|
|
||||||
{
|
|
||||||
var instance = Instantiate(implementationType);
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies[waitingType.FullName] = instance;
|
|
||||||
|
|
||||||
_waitingForInstantiation.Remove(waitingType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region run()
|
|
||||||
public ISereinIoc Run<T>(Action<T> action)
|
|
||||||
{
|
|
||||||
var service = GetOrInstantiate<T>();
|
|
||||||
if (service != null)
|
|
||||||
{
|
|
||||||
action(service);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2>(Action<T1, T2> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
|
|
||||||
action(service1, service2);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
action(service1, service2, service3);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
action(service1, service2, service3, service4);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
action(service1, service2, service3, service4, service5);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
var service6 = GetOrInstantiate<T6>();
|
|
||||||
action(service1, service2, service3, service4, service5, service6);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
var service6 = GetOrInstantiate<T6>();
|
|
||||||
var service7 = GetOrInstantiate<T7>();
|
|
||||||
action(service1, service2, service3, service4, service5, service6, service7);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
var service6 = GetOrInstantiate<T6>();
|
|
||||||
var service7 = GetOrInstantiate<T7>();
|
|
||||||
var service8 = GetOrInstantiate<T8>();
|
|
||||||
action(service1, service2, service3, service4, service5, service6, service7, service8);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* public interface IServiceContainer
|
|
||||||
{
|
|
||||||
ServiceContainer Register<T>(params object[] parameters);
|
|
||||||
ServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
|
||||||
TService Resolve<TService>();
|
|
||||||
void Get<T>(Action<T> action);
|
|
||||||
object Instantiate(Type type, params object[] parameters);
|
|
||||||
|
|
||||||
}
|
|
||||||
public class ServiceContainer : IServiceContainer
|
|
||||||
{
|
|
||||||
private readonly Dictionary<Type, object> _dependencies;
|
|
||||||
public ServiceContainer()
|
|
||||||
{
|
|
||||||
_dependencies = new Dictionary<Type, object>
|
|
||||||
{
|
|
||||||
[typeof(IServiceContainer)] = this
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Get<T>(Action<T> action)
|
|
||||||
{
|
|
||||||
var service = Resolve<T>();
|
|
||||||
action(service);
|
|
||||||
}
|
|
||||||
public ServiceContainer Register<T>(params object[] parameters)
|
|
||||||
{
|
|
||||||
var instance = Instantiate(typeof(T), parameters);
|
|
||||||
_dependencies[typeof(T)] = instance;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
|
||||||
where TImplementation : TService
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TService Resolve<TService>()
|
|
||||||
{
|
|
||||||
return (TService)_dependencies[typeof(TService)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Instantiate(Type controllerType, params object[] parameters)
|
|
||||||
{
|
|
||||||
var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数
|
|
||||||
|
|
||||||
// 查找具有最多参数的构造函数
|
|
||||||
var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
|
|
||||||
|
|
||||||
if (constructor != null)
|
|
||||||
{
|
|
||||||
if (parameters.Length > 0)
|
|
||||||
{
|
|
||||||
return Activator.CreateInstance(controllerType, parameters);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var tmpParameters = constructor.GetParameters();
|
|
||||||
var dependencyInstances = new List<object>();
|
|
||||||
|
|
||||||
foreach (var parameter in tmpParameters)
|
|
||||||
{
|
|
||||||
var parameterType = parameter.ParameterType;
|
|
||||||
_dependencies.TryGetValue(parameterType, out var dependencyInstance);
|
|
||||||
dependencyInstances.Add(dependencyInstance);
|
|
||||||
if (dependencyInstance == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 用解析的依赖项实例化目标类型
|
|
||||||
return Activator.CreateInstance(controllerType, dependencyInstances.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Activator.CreateInstance(controllerType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.LibraryCore.SerinExpression
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 条件解析抽象类
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ConditionResolver
|
|
||||||
{
|
|
||||||
public abstract bool Evaluate(object obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PassConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Pass => true,
|
|
||||||
Operator.NotPass => false,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
Pass,
|
|
||||||
NotPass,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 不进行任何操作
|
|
||||||
/// </summary>
|
|
||||||
Node,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于
|
|
||||||
/// </summary>
|
|
||||||
LessThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 等于
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于或等于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于或等于
|
|
||||||
/// </summary>
|
|
||||||
LessThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 在两者之间
|
|
||||||
/// </summary>
|
|
||||||
InRange,
|
|
||||||
/// <summary>
|
|
||||||
/// 不在两者之间
|
|
||||||
/// </summary>
|
|
||||||
OutOfRange
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
public T RangeStart { get; set; }
|
|
||||||
public T RangeEnd { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is T typedObj)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(typedObj);
|
|
||||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
|
||||||
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
|
||||||
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
|
||||||
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
|
||||||
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
|
||||||
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
|
||||||
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
|
||||||
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.GreaterThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) > 0;
|
|
||||||
case Operator.LessThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) < 0;
|
|
||||||
case Operator.Equal:
|
|
||||||
return evaluatedValue.CompareTo(Value) == 0;
|
|
||||||
case Operator.GreaterThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) >= 0;
|
|
||||||
case Operator.LessThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) <= 0;
|
|
||||||
case Operator.InRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
|
||||||
case Operator.OutOfRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoolConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 是
|
|
||||||
/// </summary>
|
|
||||||
Is
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public bool Value { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj is bool boolObj)
|
|
||||||
{
|
|
||||||
return boolObj == Value;
|
|
||||||
/*switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Is:
|
|
||||||
return boolObj == Value;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 出现过
|
|
||||||
/// </summary>
|
|
||||||
Contains,
|
|
||||||
/// <summary>
|
|
||||||
/// 没有出现过
|
|
||||||
/// </summary>
|
|
||||||
DoesNotContain,
|
|
||||||
/// <summary>
|
|
||||||
/// 相等
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 不相等
|
|
||||||
/// </summary>
|
|
||||||
NotEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 起始字符串等于
|
|
||||||
/// </summary>
|
|
||||||
StartsWith,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束字符串等于
|
|
||||||
/// </summary>
|
|
||||||
EndsWith
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is string strObj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Contains => strObj.Contains(Value),
|
|
||||||
Operator.DoesNotContain => !strObj.Contains(Value),
|
|
||||||
Operator.Equal => strObj == Value,
|
|
||||||
Operator.NotEqual => strObj != Value,
|
|
||||||
Operator.StartsWith => strObj.StartsWith(Value),
|
|
||||||
Operator.EndsWith => strObj.EndsWith(Value),
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Contains:
|
|
||||||
return strObj.Contains(Value);
|
|
||||||
case Operator.DoesNotContain:
|
|
||||||
return !strObj.Contains(Value);
|
|
||||||
case Operator.Equal:
|
|
||||||
return strObj == Value;
|
|
||||||
case Operator.NotEqual:
|
|
||||||
return strObj != Value;
|
|
||||||
case Operator.StartsWith:
|
|
||||||
return strObj.StartsWith(Value);
|
|
||||||
case Operator.EndsWith:
|
|
||||||
return strObj.EndsWith(Value);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
//public string MemberPath { get; set; }
|
|
||||||
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
|
||||||
public object? TargetObj { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object? obj)
|
|
||||||
{
|
|
||||||
//object? memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (TargetObj is T typedObj)
|
|
||||||
{
|
|
||||||
return new ValueTypeConditionResolver<T>
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value,
|
|
||||||
ArithmeticExpression = ArithmeticExpression,
|
|
||||||
}.Evaluate(typedObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//private object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
//{
|
|
||||||
// string[] members = memberPath[1..].Split('.');
|
|
||||||
// foreach (var member in members)
|
|
||||||
// {
|
|
||||||
// if (obj == null) return null;
|
|
||||||
// Type type = obj.GetType();
|
|
||||||
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
// FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// obj = propertyInfo.GetValue(obj);
|
|
||||||
// else if (fieldInfo != null)
|
|
||||||
// obj = fieldInfo.GetValue(obj);
|
|
||||||
// else
|
|
||||||
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MemberStringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
|
|
||||||
public string MemberPath { get; set; }
|
|
||||||
|
|
||||||
public StringConditionResolver.Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
object memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (memberValue is string strObj)
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value
|
|
||||||
}.Evaluate(strObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,338 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.LibraryCore.SerinExpression;
|
|
||||||
|
|
||||||
public class SerinConditionParser
|
|
||||||
{
|
|
||||||
public static bool To<T>(T data, string expression)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
return ConditionParse(data, expression).Evaluate(data);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConditionResolver ConditionParse(object data, string expression)
|
|
||||||
{
|
|
||||||
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
|
||||||
{
|
|
||||||
return ParseObjectExpression(data, expression);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ParseSimpleExpression(data, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//bool ContainsArithmeticOperators(string expression)
|
|
||||||
//{
|
|
||||||
// return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取计算表达式的部分
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="part"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对象指定名称的成员
|
|
||||||
/// </summary>
|
|
||||||
private static object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
if (obj == null) return null;
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 解析对象表达式
|
|
||||||
/// </summary>
|
|
||||||
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
int typeStartIndex = expression.IndexOf('<');
|
|
||||||
int typeEndIndex = expression.IndexOf('>');
|
|
||||||
|
|
||||||
string memberPath;
|
|
||||||
Type type;
|
|
||||||
object? targetObj;
|
|
||||||
if (typeStartIndex + typeStartIndex == -2)
|
|
||||||
{
|
|
||||||
memberPath = operatorStr;
|
|
||||||
targetObj = GetMemberValue(data, operatorStr);
|
|
||||||
|
|
||||||
type = targetObj.GetType();
|
|
||||||
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (typeStartIndex >= typeEndIndex)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("无效的表达式格式");
|
|
||||||
}
|
|
||||||
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
|
||||||
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
|
||||||
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
|
||||||
if (parts.Length == 3)
|
|
||||||
{
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operatorStr = parts[0].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(1));
|
|
||||||
}
|
|
||||||
targetObj = GetMemberValue(data, memberPath);
|
|
||||||
|
|
||||||
Type? tempType = typeStr switch
|
|
||||||
{
|
|
||||||
"int" => typeof(int),
|
|
||||||
"double" => typeof(double),
|
|
||||||
"bool" => typeof(bool),
|
|
||||||
"string" => typeof(string),
|
|
||||||
_ => Type.GetType(typeStr)
|
|
||||||
};
|
|
||||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<int>
|
|
||||||
{
|
|
||||||
TargetObj = targetObj,
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
Op = ParseValueTypeOperator<int>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<double>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
return new MemberConditionResolver<bool>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new MemberStringConditionResolver
|
|
||||||
{
|
|
||||||
MemberPath = memberPath,
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
if ("pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.Pass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ("not pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if ("!pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
|
|
||||||
if (parts.Length < 2)
|
|
||||||
throw new ArgumentException("无效的表达式格式。");
|
|
||||||
|
|
||||||
//string typeStr = parts[0];
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
Type type = data.GetType();//Type.GetType(typeStr);
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
|
||||||
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
|
||||||
{
|
|
||||||
var temp = valueStr.Split('-');
|
|
||||||
if (temp.Length < 2)
|
|
||||||
throw new ArgumentException($"范围无效:{valueStr}。");
|
|
||||||
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
|
|
||||||
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
RangeStart = rangeStart,
|
|
||||||
RangeEnd = rangeEnd,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<double>
|
|
||||||
{
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
bool value = bool.Parse(valueStr);
|
|
||||||
return new BoolConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseBoolOperator(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
|
||||||
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
|
||||||
"=" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
|
|
||||||
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"is" => BoolConditionResolver.Operator.Is,
|
|
||||||
"==" => BoolConditionResolver.Operator.Is,
|
|
||||||
"equals" => BoolConditionResolver.Operator.Is,
|
|
||||||
//"isFalse" => BoolConditionNode.Operator.IsFalse,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"c" => StringConditionResolver.Operator.Contains,
|
|
||||||
"nc" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"sw" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"ew" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
|
|
||||||
"contains" => StringConditionResolver.Operator.Contains,
|
|
||||||
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"equals" => StringConditionResolver.Operator.Equal,
|
|
||||||
"==" => StringConditionResolver.Operator.Equal,
|
|
||||||
"notEquals" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"!=" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"startsWith" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"endsWith" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,418 +0,0 @@
|
|||||||
//using Serein.Web;
|
|
||||||
//using Microsoft.Win32;
|
|
||||||
//using NetTaste;
|
|
||||||
//using System.Collections.Concurrent;
|
|
||||||
//using System.Reflection;
|
|
||||||
//using System;
|
|
||||||
//using System.Linq;
|
|
||||||
//using System.Collections.Generic;
|
|
||||||
//using SqlSugar;
|
|
||||||
|
|
||||||
//namespace Serein
|
|
||||||
//{
|
|
||||||
|
|
||||||
|
|
||||||
// public interface IServiceContainer
|
|
||||||
// {
|
|
||||||
// /// <summary>
|
|
||||||
// /// 获取或创建类型的实例(不注入依赖项)
|
|
||||||
// /// </summary>
|
|
||||||
// object GetOrCreateServiceInstance(Type serviceType, params object[] parameters);
|
|
||||||
// T CreateServiceInstance<T>(params object[] parameters);
|
|
||||||
// IServiceContainer Reset(); // 清空
|
|
||||||
// IServiceContainer Register(Type type, params object[] parameters);
|
|
||||||
// IServiceContainer Register<T>(params object[] parameters);
|
|
||||||
// IServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
|
||||||
// T Get<T>();
|
|
||||||
// object Get(Type type);
|
|
||||||
|
|
||||||
// /// <summary>
|
|
||||||
// /// 创建目标类型的对象, 并注入依赖项
|
|
||||||
// /// </summary>
|
|
||||||
// object? Instantiate(Type type, params object[] parameters);
|
|
||||||
// IServiceContainer Build();
|
|
||||||
// IServiceContainer Run<T>(Action<T> action);
|
|
||||||
// IServiceContainer Run<T1, T2>(Action<T1, T2> action);
|
|
||||||
// IServiceContainer Run<T1, T2, T3>(Action<T1, T2, T3> action);
|
|
||||||
// IServiceContainer Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action);
|
|
||||||
// IServiceContainer Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action);
|
|
||||||
// IServiceContainer Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action);
|
|
||||||
// IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action);
|
|
||||||
// IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public class ServiceContainer : IServiceContainer
|
|
||||||
// {
|
|
||||||
|
|
||||||
// private readonly ConcurrentDictionary<string, object> _dependencies;
|
|
||||||
// private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
|
||||||
// private readonly List<Type> _waitingForInstantiation;
|
|
||||||
|
|
||||||
// public ServiceContainer()
|
|
||||||
// {
|
|
||||||
|
|
||||||
// _dependencies = new ConcurrentDictionary<string, object>
|
|
||||||
// {
|
|
||||||
// [typeof(IServiceContainer).FullName] = this
|
|
||||||
// };
|
|
||||||
|
|
||||||
// _typeMappings = new ConcurrentDictionary<string, Type>();
|
|
||||||
// _waitingForInstantiation = [];
|
|
||||||
// }
|
|
||||||
// public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
|
||||||
// {
|
|
||||||
// Register(type);
|
|
||||||
// object instance;
|
|
||||||
|
|
||||||
// if (_dependencies.ContainsKey(type.FullName))
|
|
||||||
// {
|
|
||||||
// instance = _dependencies[type.FullName];
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
|
|
||||||
// instance = Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
|
|
||||||
// _dependencies[type.FullName] = instance;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// return instance;
|
|
||||||
|
|
||||||
// }
|
|
||||||
// public T CreateServiceInstance<T>(params object[] parameters)
|
|
||||||
// {
|
|
||||||
// return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Reset()
|
|
||||||
// {
|
|
||||||
// foreach(var instancei in _dependencies.Values)
|
|
||||||
// {
|
|
||||||
// if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
|
||||||
// {
|
|
||||||
// disposable.Dispose();
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// _dependencies.Clear();
|
|
||||||
// _waitingForInstantiation.Clear();
|
|
||||||
// //_typeMappings.Clear();
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Register(Type type, params object[] parameters)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// if (!_typeMappings.ContainsKey(type.FullName))
|
|
||||||
// {
|
|
||||||
// _typeMappings[type.FullName] = type;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
// public IServiceContainer Register<T>(params object[] parameters)
|
|
||||||
// {
|
|
||||||
// Register(typeof(T), parameters);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
|
||||||
// where TImplementation : TService
|
|
||||||
// {
|
|
||||||
// _typeMappings[typeof(TService).FullName!] = typeof(TImplementation);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public object Get(Type type)
|
|
||||||
// {
|
|
||||||
|
|
||||||
|
|
||||||
// if (!_dependencies.TryGetValue(type.FullName, out object value))
|
|
||||||
// {
|
|
||||||
// Register(type);
|
|
||||||
|
|
||||||
// value = Instantiate(type);
|
|
||||||
|
|
||||||
// InjectDependencies(type);
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// return value;
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// public T Get<T>()
|
|
||||||
// {
|
|
||||||
|
|
||||||
|
|
||||||
// if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
|
||||||
// {
|
|
||||||
// Register<T>();
|
|
||||||
|
|
||||||
// value = Instantiate(typeof(T));
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// return (T)value;
|
|
||||||
|
|
||||||
|
|
||||||
// //throw new InvalidOperationException("目标类型未创建实例");
|
|
||||||
// }
|
|
||||||
// public IServiceContainer Build()
|
|
||||||
// {
|
|
||||||
// foreach (var type in _typeMappings.Values)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// if(!_dependencies.ContainsKey(type.FullName))
|
|
||||||
// {
|
|
||||||
|
|
||||||
// _dependencies[type.FullName] = Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// foreach (var instance in _dependencies.Values)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// InjectDependencies(instance); // 替换占位符
|
|
||||||
// }
|
|
||||||
|
|
||||||
// //var instance = Instantiate(item.Value);
|
|
||||||
|
|
||||||
// TryInstantiateWaitingDependencies();
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public object? Instantiate(Type controllerType, params object[] parameters)
|
|
||||||
// {
|
|
||||||
// var instance = Activator.CreateInstance(controllerType, parameters);
|
|
||||||
// if(instance != null)
|
|
||||||
// {
|
|
||||||
// InjectDependencies(instance);
|
|
||||||
// }
|
|
||||||
// return instance;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private void InjectDependencies(object instance)
|
|
||||||
// {
|
|
||||||
// var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public)
|
|
||||||
// .Where(p => p.CanWrite && p.GetCustomAttribute<AutoInjectionAttribute>() != null);
|
|
||||||
|
|
||||||
// foreach (var property in properties)
|
|
||||||
// {
|
|
||||||
// var propertyType = property.PropertyType;
|
|
||||||
|
|
||||||
// if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
|
||||||
// {
|
|
||||||
// property.SetValue(instance, dependencyInstance);
|
|
||||||
// }
|
|
||||||
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// private void TryInstantiateWaitingDependencies()
|
|
||||||
// {
|
|
||||||
// foreach (var waitingType in _waitingForInstantiation.ToList())
|
|
||||||
// {
|
|
||||||
// if (_typeMappings.TryGetValue(waitingType.FullName!, out var implementationType))
|
|
||||||
// {
|
|
||||||
// var instance = Instantiate(implementationType);
|
|
||||||
// if (instance != null)
|
|
||||||
// {
|
|
||||||
|
|
||||||
// _dependencies[waitingType.FullName] = instance;
|
|
||||||
|
|
||||||
// _waitingForInstantiation.Remove(waitingType);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #region run()
|
|
||||||
// public IServiceContainer Run<T>(Action<T> action)
|
|
||||||
// {
|
|
||||||
// var service = Get<T>();
|
|
||||||
// if (service != null)
|
|
||||||
// {
|
|
||||||
// action(service);
|
|
||||||
// }
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2>(Action<T1, T2> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
|
|
||||||
// action(service1, service2);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
// var service3 = Get<T3>();
|
|
||||||
// action(service1, service2, service3);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
// var service3 = Get<T3>();
|
|
||||||
// var service4 = Get<T4>();
|
|
||||||
// action(service1, service2, service3, service4);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
// var service3 = Get<T3>();
|
|
||||||
// var service4 = Get<T4>();
|
|
||||||
// var service5 = Get<T5>();
|
|
||||||
// action(service1, service2, service3, service4, service5);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
// var service3 = Get<T3>();
|
|
||||||
// var service4 = Get<T4>();
|
|
||||||
// var service5 = Get<T5>();
|
|
||||||
// var service6 = Get<T6>();
|
|
||||||
// action(service1, service2, service3, service4, service5, service6);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
// var service3 = Get<T3>();
|
|
||||||
// var service4 = Get<T4>();
|
|
||||||
// var service5 = Get<T5>();
|
|
||||||
// var service6 = Get<T6>();
|
|
||||||
// var service7 = Get<T7>();
|
|
||||||
// action(service1, service2, service3, service4, service5, service6, service7);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public IServiceContainer Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
|
||||||
// {
|
|
||||||
// var service1 = Get<T1>();
|
|
||||||
// var service2 = Get<T2>();
|
|
||||||
// var service3 = Get<T3>();
|
|
||||||
// var service4 = Get<T4>();
|
|
||||||
// var service5 = Get<T5>();
|
|
||||||
// var service6 = Get<T6>();
|
|
||||||
// var service7 = Get<T7>();
|
|
||||||
// var service8 = Get<T8>();
|
|
||||||
// action(service1, service2, service3, service4, service5, service6, service7, service8);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// #endregion
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// /* public interface IServiceContainer
|
|
||||||
// {
|
|
||||||
// ServiceContainer Register<T>(params object[] parameters);
|
|
||||||
// ServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
|
||||||
// TService Resolve<TService>();
|
|
||||||
// void Get<T>(Action<T> action);
|
|
||||||
// object Instantiate(Type type, params object[] parameters);
|
|
||||||
|
|
||||||
// }
|
|
||||||
// public class ServiceContainer : IServiceContainer
|
|
||||||
// {
|
|
||||||
// private readonly Dictionary<Type, object> _dependencies;
|
|
||||||
// public ServiceContainer()
|
|
||||||
// {
|
|
||||||
// _dependencies = new Dictionary<Type, object>
|
|
||||||
// {
|
|
||||||
// [typeof(IServiceContainer)] = this
|
|
||||||
// };
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public void Get<T>(Action<T> action)
|
|
||||||
// {
|
|
||||||
// var service = Resolve<T>();
|
|
||||||
// action(service);
|
|
||||||
// }
|
|
||||||
// public ServiceContainer Register<T>(params object[] parameters)
|
|
||||||
// {
|
|
||||||
// var instance = Instantiate(typeof(T), parameters);
|
|
||||||
// _dependencies[typeof(T)] = instance;
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public ServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
|
||||||
// where TImplementation : TService
|
|
||||||
// {
|
|
||||||
|
|
||||||
// _dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters);
|
|
||||||
// return this;
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// public TService Resolve<TService>()
|
|
||||||
// {
|
|
||||||
// return (TService)_dependencies[typeof(TService)];
|
|
||||||
// }
|
|
||||||
|
|
||||||
// public object Instantiate(Type controllerType, params object[] parameters)
|
|
||||||
// {
|
|
||||||
// var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数
|
|
||||||
|
|
||||||
// // 查找具有最多参数的构造函数
|
|
||||||
// var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
|
|
||||||
|
|
||||||
// if (constructor != null)
|
|
||||||
// {
|
|
||||||
// if (parameters.Length > 0)
|
|
||||||
// {
|
|
||||||
// return Activator.CreateInstance(controllerType, parameters);
|
|
||||||
// }
|
|
||||||
// else {
|
|
||||||
// var tmpParameters = constructor.GetParameters();
|
|
||||||
// var dependencyInstances = new List<object>();
|
|
||||||
|
|
||||||
// foreach (var parameter in tmpParameters)
|
|
||||||
// {
|
|
||||||
// var parameterType = parameter.ParameterType;
|
|
||||||
// _dependencies.TryGetValue(parameterType, out var dependencyInstance);
|
|
||||||
// dependencyInstances.Add(dependencyInstance);
|
|
||||||
// if (dependencyInstance == null)
|
|
||||||
// {
|
|
||||||
// return null;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // 用解析的依赖项实例化目标类型
|
|
||||||
// return Activator.CreateInstance(controllerType, dependencyInstances.ToArray());
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// return Activator.CreateInstance(controllerType);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
//}
|
|
||||||
@@ -1,185 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Tool
|
|
||||||
{
|
|
||||||
public static class DataHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 把Object转换为Json字符串
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToJson(this object obj)
|
|
||||||
{
|
|
||||||
IsoDateTimeConverter val = new IsoDateTimeConverter();
|
|
||||||
val.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
IsoDateTimeConverter val2 = val;
|
|
||||||
return JsonConvert.SerializeObject(obj, (JsonConverter[])(object)new JsonConverter[1] { (JsonConverter)val2 });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 把Json文本转为实体
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static T FromJSON<T>(this string input)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (typeof(T).IsAssignableFrom(typeof(T)))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<T>(input);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.Message);
|
|
||||||
// return default(T);
|
|
||||||
|
|
||||||
return default;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<T> IListToList<T>(IList list)
|
|
||||||
{
|
|
||||||
T[] array = new T[list.Count];
|
|
||||||
list.CopyTo(array, 0);
|
|
||||||
return new List<T>(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataTable GetNewDataTable(DataTable dt, string condition)
|
|
||||||
{
|
|
||||||
if (!IsExistRows(dt))
|
|
||||||
{
|
|
||||||
if (condition.Trim() == "")
|
|
||||||
{
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataTable dataTable = new DataTable();
|
|
||||||
dataTable = dt.Clone();
|
|
||||||
DataRow[] array = dt.Select(condition);
|
|
||||||
for (int i = 0; i < array.Length; i++)
|
|
||||||
{
|
|
||||||
dataTable.ImportRow(array[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsExistRows(DataTable dt)
|
|
||||||
{
|
|
||||||
if (dt != null && dt.Rows.Count > 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Hashtable DataTableToHashtable(DataTable dt)
|
|
||||||
{
|
|
||||||
Hashtable hashtable = new Hashtable();
|
|
||||||
foreach (DataRow row in dt.Rows)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < dt.Columns.Count; i++)
|
|
||||||
{
|
|
||||||
string columnName = dt.Columns[i].ColumnName;
|
|
||||||
hashtable[columnName] = row[columnName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashtable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataTable ListToDataTable<T>(List<T> entitys)
|
|
||||||
{
|
|
||||||
if (entitys == null || entitys.Count < 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Type type = entitys[0].GetType();
|
|
||||||
|
|
||||||
PropertyInfo[] properties = type.GetProperties();
|
|
||||||
DataTable dataTable = new DataTable();
|
|
||||||
for (int i = 0; i < properties.Length; i++)
|
|
||||||
{
|
|
||||||
dataTable.Columns.Add(properties[i].Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (T entity in entitys)
|
|
||||||
{
|
|
||||||
|
|
||||||
object obj = entity;
|
|
||||||
|
|
||||||
|
|
||||||
if (obj.GetType() != type)
|
|
||||||
{
|
|
||||||
throw new Exception("要转换的集合元素类型不一致");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
object[] array = new object[properties.Length];
|
|
||||||
for (int j = 0; j < properties.Length; j++)
|
|
||||||
{
|
|
||||||
|
|
||||||
array[j] = properties[j].GetValue(obj, null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
dataTable.Rows.Add(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string DataTableToXML(DataTable dt)
|
|
||||||
{
|
|
||||||
if (dt != null && dt.Rows.Count > 0)
|
|
||||||
{
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
|
||||||
dt.WriteXml((TextWriter)stringWriter);
|
|
||||||
return stringWriter.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string DataSetToXML(DataSet ds)
|
|
||||||
{
|
|
||||||
if (ds != null)
|
|
||||||
{
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
|
||||||
ds.WriteXml((TextWriter)stringWriter);
|
|
||||||
return stringWriter.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,167 +0,0 @@
|
|||||||
using Serein.Library.Framework.NodeFlow;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.Http
|
|
||||||
{
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
|
||||||
///// </summary>
|
|
||||||
//[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
//public class DynamicFlowAttribute : Attribute
|
|
||||||
//{
|
|
||||||
// public DynamicFlowAttribute(bool scan = true)
|
|
||||||
// {
|
|
||||||
// Scan = scan;
|
|
||||||
// }
|
|
||||||
// public bool Scan { get; set; }
|
|
||||||
//}
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
|
||||||
///// </summary>
|
|
||||||
//[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
|
|
||||||
//public class MethodDetailAttribute : Attribute
|
|
||||||
//{
|
|
||||||
|
|
||||||
// public MethodDetailAttribute(DynamicNodeType methodDynamicType,
|
|
||||||
// string methodTips = "",
|
|
||||||
// bool scan = true,
|
|
||||||
// string lockName = "")
|
|
||||||
// {
|
|
||||||
// Scan = scan;
|
|
||||||
// MethodDynamicType = methodDynamicType;
|
|
||||||
// MethodTips = methodTips;
|
|
||||||
// LockName = lockName;
|
|
||||||
// }
|
|
||||||
// public bool Scan { get; set; }
|
|
||||||
// public string MethodTips { get; }
|
|
||||||
// public DynamicNodeType MethodDynamicType { get; }
|
|
||||||
// public string LockName { get; }
|
|
||||||
//}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为显式参数
|
|
||||||
/// </summary>
|
|
||||||
//[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
//public class ExplicitAttribute : Attribute // where TEnum : Enum
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表示参数为url中的数据(Get请求中不需要显式标注)
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
public sealed class IsUrlDataAttribute : Attribute
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表示入参参数为整个boby的数据
|
|
||||||
/// <para>
|
|
||||||
/// 例如:User类型含有int id、string name字段</para>
|
|
||||||
/// <para>
|
|
||||||
/// ① Add(User user)</para>
|
|
||||||
/// <para>请求需要传入的json为
|
|
||||||
/// {"user":{
|
|
||||||
/// "id":2,
|
|
||||||
/// "name":"李志忠"}}</para>
|
|
||||||
/// <para>
|
|
||||||
/// ② Add([Boby]User user)</para>
|
|
||||||
/// <para>请求需要传入的json为
|
|
||||||
/// {"id":2,"name":"李志忠"}</para>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
public sealed class IsBobyDataAttribute : Attribute
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 表示该控制器会被自动注册(与程序集同一命名空间,暂时不支持运行时自动加载DLL,需要手动注册)
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public sealed class AutoHostingAttribute : Attribute
|
|
||||||
{
|
|
||||||
public AutoHostingAttribute(string url = "")
|
|
||||||
{
|
|
||||||
Url = url;
|
|
||||||
}
|
|
||||||
public string Url { get; }
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 表示该属性为自动注入依赖项
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
|
||||||
public sealed class AutoInjectionAttribute : Attribute
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法的接口类型与附加URL
|
|
||||||
/// </summary>
|
|
||||||
/// <remarks>
|
|
||||||
/// 假设UserController.Add()的WebAPI特性中
|
|
||||||
/// http是HTTP.POST
|
|
||||||
/// url被显示标明“temp”
|
|
||||||
/// 那么请求的接口是POST,URL是
|
|
||||||
/// [http://localhost:8080]/user/add/temp
|
|
||||||
/// </remarks>
|
|
||||||
/// <param name="http"></param>
|
|
||||||
/// <param name="url"></param>
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
|
||||||
|
|
||||||
public sealed class WebApiAttribute : Attribute
|
|
||||||
|
|
||||||
{
|
|
||||||
public API Type;
|
|
||||||
public string Url;
|
|
||||||
/// <summary>
|
|
||||||
/// 方法名称不作为url的部分
|
|
||||||
/// </summary>
|
|
||||||
public bool IsUrl;
|
|
||||||
}
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
|
||||||
|
|
||||||
public sealed class ApiPostAttribute : Attribute
|
|
||||||
|
|
||||||
{
|
|
||||||
public string Url;
|
|
||||||
/// <summary>
|
|
||||||
/// 方法名称不作为url的部分
|
|
||||||
/// </summary>
|
|
||||||
public bool IsUrl = true;
|
|
||||||
}
|
|
||||||
[AttributeUsage(AttributeTargets.Method)]
|
|
||||||
|
|
||||||
public sealed class ApiGetAttribute : Attribute
|
|
||||||
|
|
||||||
{
|
|
||||||
public string Url;
|
|
||||||
/// <summary>
|
|
||||||
/// 方法名称不作为url的部分
|
|
||||||
/// </summary>
|
|
||||||
public bool IsUrl = true;
|
|
||||||
}
|
|
||||||
/*public sealed class WebApiAttribute(API http, bool isUrl = true, string url = "") : Attribute
|
|
||||||
{
|
|
||||||
public API Http { get; } = http;
|
|
||||||
public string Url { get; } = url;
|
|
||||||
/// <summary>
|
|
||||||
/// 方法名称不作为url的部分
|
|
||||||
/// </summary>
|
|
||||||
public bool IsUrl { get; } = isUrl;
|
|
||||||
}*/
|
|
||||||
public enum API
|
|
||||||
{
|
|
||||||
POST,
|
|
||||||
GET,
|
|
||||||
//PUT,
|
|
||||||
//DELETE
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
using System;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.Http
|
|
||||||
{
|
|
||||||
public class ControllerBase
|
|
||||||
{
|
|
||||||
|
|
||||||
public string Url { get; set; }
|
|
||||||
|
|
||||||
public string BobyData { get; set; }
|
|
||||||
|
|
||||||
public string GetLog(Exception ex)
|
|
||||||
{
|
|
||||||
return "Url : " + Url + Environment.NewLine +
|
|
||||||
"Ex : " + ex.Message + Environment.NewLine +
|
|
||||||
"Data : " + BobyData + Environment.NewLine;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,761 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Linq;
|
|
||||||
using Serein.Library.Api.Api;
|
|
||||||
using Serein.Library.Framework.IOC;
|
|
||||||
using Serein.Tool;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Net;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.Remoting.Messaging;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using System.Web;
|
|
||||||
using Enum = System.Enum;
|
|
||||||
using Type = System.Type;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.Http
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 路由注册与解析
|
|
||||||
/// </summary>
|
|
||||||
public class Router
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, bool> _controllerAutoHosting; // 存储是否实例化
|
|
||||||
private readonly ConcurrentDictionary<string, Type> _controllerTypes; // 存储控制器类型
|
|
||||||
private readonly ConcurrentDictionary<string, object> _controllerInstances; // 存储控制器实例对象
|
|
||||||
private readonly ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>> _routes; // 用于存储路由信息
|
|
||||||
|
|
||||||
private readonly ISereinIoc serviceRegistry; // 用于存储路由信息
|
|
||||||
|
|
||||||
//private Type PostRequest;
|
|
||||||
|
|
||||||
public Router(ISereinIoc serviceRegistry) // 构造函数,初始化 Router 类的新实例
|
|
||||||
{
|
|
||||||
this.serviceRegistry = serviceRegistry;
|
|
||||||
|
|
||||||
_routes = new ConcurrentDictionary<string, ConcurrentDictionary<string, MethodInfo>>(); // 初始化路由字典
|
|
||||||
|
|
||||||
_controllerAutoHosting = new ConcurrentDictionary<string, bool>(); // 初始化控制器实例对象字典
|
|
||||||
_controllerTypes = new ConcurrentDictionary<string, Type>(); // 初始化控制器实例对象字典
|
|
||||||
_controllerInstances = new ConcurrentDictionary<string, object>(); // 初始化控制器实例对象字典
|
|
||||||
|
|
||||||
foreach (API method in Enum.GetValues(typeof(API))) // 遍历 HTTP 枚举类型的所有值
|
|
||||||
{
|
|
||||||
_routes.TryAdd(method.ToString(), new ConcurrentDictionary<string, MethodInfo>()); // 初始化每种 HTTP 方法对应的路由字典
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前程序集
|
|
||||||
Assembly assembly = Assembly.GetExecutingAssembly();
|
|
||||||
|
|
||||||
// 获取包含“Controller”名称的类型
|
|
||||||
var controllerTypes = assembly.GetTypes()
|
|
||||||
.Where(t => t.Name.Contains("Controller"));
|
|
||||||
|
|
||||||
Type baseAttribute = typeof(AutoHostingAttribute);
|
|
||||||
Type baseController = typeof(ControllerBase);
|
|
||||||
foreach (var controllerType in controllerTypes)
|
|
||||||
{
|
|
||||||
if (controllerType.IsSubclassOf(baseController) && controllerType.IsDefined(baseAttribute))
|
|
||||||
{
|
|
||||||
|
|
||||||
// 如果属于控制器,并标记了AutoHosting特性,进行自动注册
|
|
||||||
AutoRegisterAutoController(controllerType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 自动注册 自动实例化控制器 类型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controllerType"></param>
|
|
||||||
public void AutoRegisterAutoController(Type controllerType) // 方法声明,用于注册并实例化控制器类型
|
|
||||||
{
|
|
||||||
if (!controllerType.IsClass || controllerType.IsAbstract) return; // 如果不是类或者是抽象类,则直接返回
|
|
||||||
|
|
||||||
var autoHostingAttribute = controllerType.GetCustomAttribute<AutoHostingAttribute>();
|
|
||||||
if (autoHostingAttribute != null) {
|
|
||||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
|
||||||
{
|
|
||||||
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
|
||||||
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
|
||||||
if( apiGetAttribute == null && apiPostAttribute == null )
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
|
||||||
{
|
|
||||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
|
||||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
|
||||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (apiPostAttribute != null) // 如果存在 WebAPIAttribute 属性
|
|
||||||
{
|
|
||||||
var url = AddRoutesUrl(autoHostingAttribute,
|
|
||||||
webApiAttribute,
|
|
||||||
controllerType, method);
|
|
||||||
Console.WriteLine(url);
|
|
||||||
if (url == null) continue;
|
|
||||||
_controllerAutoHosting[url] = true;
|
|
||||||
_controllerTypes[url] = controllerType;
|
|
||||||
|
|
||||||
_controllerInstances[url] = null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
|
||||||
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
|
||||||
{
|
|
||||||
var url = AddRoutesUrl(autoHostingAttribute, routeAttribute, controllerType, method);
|
|
||||||
Console.WriteLine(url);
|
|
||||||
if (url == null) continue;
|
|
||||||
_controllerAutoHosting[url] = true;
|
|
||||||
_controllerTypes[url] = controllerType;
|
|
||||||
_controllerInstances[url] = null;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 手动注册 自动实例化控制器实例
|
|
||||||
/// </summary>
|
|
||||||
public void RegisterAutoController<T>() // 方法声明,用于动态注册路由
|
|
||||||
{
|
|
||||||
Type controllerType = typeof(T); // 获取控制器实例的类型
|
|
||||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
|
||||||
{
|
|
||||||
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
|
||||||
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
|
||||||
if (apiGetAttribute == null && apiPostAttribute == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
|
||||||
{
|
|
||||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
|
||||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
|
||||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
|
||||||
|
|
||||||
if (url == null) continue;
|
|
||||||
_controllerAutoHosting[url] = true;
|
|
||||||
_controllerTypes[url] = controllerType;
|
|
||||||
|
|
||||||
_controllerInstances[url] = null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 手动注册 实例持久控制器实例
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controllerInstance"></param>
|
|
||||||
public void RegisterController<TController>(TController controllerInstance) where TController : ControllerBase // 方法声明,用于动态注册路由
|
|
||||||
{
|
|
||||||
if(controllerInstance == null) return;
|
|
||||||
Type controllerType = controllerInstance.GetType(); // 获取控制器实例的类型
|
|
||||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
|
||||||
{
|
|
||||||
var apiGetAttribute = method.GetCustomAttribute<ApiGetAttribute>();
|
|
||||||
var apiPostAttribute = method.GetCustomAttribute<ApiPostAttribute>();
|
|
||||||
if (apiGetAttribute == null && apiPostAttribute == null)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
WebApiAttribute webApiAttribute = new WebApiAttribute()
|
|
||||||
{
|
|
||||||
Type = apiGetAttribute != null ? API.GET : API.POST,
|
|
||||||
Url = apiGetAttribute != null ? apiGetAttribute.Url : apiPostAttribute.Url,
|
|
||||||
IsUrl = apiGetAttribute != null ? apiGetAttribute.IsUrl : apiPostAttribute.IsUrl,
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var url = AddRoutesUrl(null, webApiAttribute, controllerType, method);
|
|
||||||
|
|
||||||
if (url == null) continue;
|
|
||||||
_controllerInstances[url] = controllerInstance;
|
|
||||||
_controllerAutoHosting[url] = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 从方法中收集路由信息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controllerType"></param>
|
|
||||||
public string AddRoutesUrl(AutoHostingAttribute autoHostingAttribute, WebApiAttribute webAttribute, Type controllerType, MethodInfo method)
|
|
||||||
{
|
|
||||||
string controllerName;
|
|
||||||
if (autoHostingAttribute == null || string.IsNullOrWhiteSpace(autoHostingAttribute.Url))
|
|
||||||
{
|
|
||||||
controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
controllerName = autoHostingAttribute.Url;
|
|
||||||
}
|
|
||||||
|
|
||||||
var httpMethod = webAttribute.Type; // 获取 HTTP 方法
|
|
||||||
var customUrl = webAttribute.Url; // 获取自定义 URL
|
|
||||||
|
|
||||||
string url;
|
|
||||||
|
|
||||||
if (webAttribute.IsUrl)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
|
||||||
{
|
|
||||||
url = $"/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
customUrl = CleanUrl(customUrl);
|
|
||||||
url = $"/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
|
||||||
}
|
|
||||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
|
||||||
{
|
|
||||||
url = $"/{controllerName}".ToLower(); // 构建默认 URL
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
customUrl = CleanUrl(customUrl);
|
|
||||||
url = $"/{controllerName}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
|
||||||
}
|
|
||||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
|
||||||
}
|
|
||||||
|
|
||||||
return url;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 收集路由信息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="controllerType"></param>
|
|
||||||
public void CollectRoutes(Type controllerType)
|
|
||||||
{
|
|
||||||
string controllerName = controllerType.Name.Replace("Controller", "").ToLower(); // 获取控制器名称并转换为小写
|
|
||||||
foreach (var method in controllerType.GetMethods()) // 遍历控制器类型的所有方法
|
|
||||||
{
|
|
||||||
var routeAttribute = method.GetCustomAttribute<WebApiAttribute>(); // 获取方法上的 WebAPIAttribute 自定义属性
|
|
||||||
if (routeAttribute != null) // 如果存在 WebAPIAttribute 属性
|
|
||||||
{
|
|
||||||
var customUrl = routeAttribute.Url; // 获取自定义 URL
|
|
||||||
string url;
|
|
||||||
if (string.IsNullOrEmpty(customUrl)) // 如果自定义 URL 为空
|
|
||||||
{
|
|
||||||
url = $"/api/{controllerName}/{method.Name}".ToLower(); // 构建默认 URL
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
customUrl = CleanUrl(customUrl);
|
|
||||||
url = $"/api/{controllerName}/{method.Name}/{customUrl}".ToLower();// 清理自定义 URL,并构建新的 URL
|
|
||||||
}
|
|
||||||
var httpMethod = routeAttribute.Type; // 获取 HTTP 方法
|
|
||||||
_routes[httpMethod.ToString()].TryAdd(url, method); // 将 URL 和方法添加到对应的路由字典中
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析路由,调用对应的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task<bool> RouteAsync(HttpListenerContext context)
|
|
||||||
{
|
|
||||||
var request = context.Request; // 获取请求对象
|
|
||||||
var response = context.Response; // 获取响应对象
|
|
||||||
var url = request.Url; // 获取请求的 URL
|
|
||||||
var httpMethod = request.HttpMethod; // 获取请求的 HTTP 方法
|
|
||||||
|
|
||||||
var template = request.Url.AbsolutePath.ToLower();
|
|
||||||
|
|
||||||
|
|
||||||
if (!_routes[httpMethod].TryGetValue(template, out MethodInfo method))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
var routeValues = GetUrlData(url); // 解析 URL 获取路由参数
|
|
||||||
|
|
||||||
ControllerBase controllerInstance;
|
|
||||||
if (!_controllerAutoHosting[template])
|
|
||||||
{
|
|
||||||
controllerInstance = (ControllerBase)_controllerInstances[template];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
controllerInstance = (ControllerBase)serviceRegistry.Instantiate(_controllerTypes[template]);// 使用反射创建控制器实例
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
if (controllerInstance == null)
|
|
||||||
{
|
|
||||||
return false; // 未找到控制器实例
|
|
||||||
}
|
|
||||||
|
|
||||||
controllerInstance.Url = url.AbsolutePath;
|
|
||||||
object result;
|
|
||||||
switch (httpMethod) // 根据请求的 HTTP 方法执行不同的操作
|
|
||||||
{
|
|
||||||
case "GET": // 如果是 GET 请求,传入方法、控制器、url参数
|
|
||||||
result = InvokeControllerMethodWithRouteValues(method, controllerInstance, routeValues);
|
|
||||||
break;
|
|
||||||
case "POST": // POST 请求传入方法、控制器、请求体内容,url参数
|
|
||||||
var requestBody = await ReadRequestBodyAsync(request); // 读取请求体内容
|
|
||||||
controllerInstance.BobyData = requestBody;
|
|
||||||
var requestJObject = requestBody.FromJSON<object>();
|
|
||||||
|
|
||||||
result = InvokeControllerMethod(method, controllerInstance, requestJObject, routeValues);
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
|
|
||||||
result = null;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
Return(response, result); // 返回结果
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string GetLog(string Url, string BobyData = "")
|
|
||||||
{
|
|
||||||
return Environment.NewLine +
|
|
||||||
"Url : " + Url + Environment.NewLine +
|
|
||||||
"Data : " + BobyData + Environment.NewLine;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// GET请求的控制器方法
|
|
||||||
/// </summary>
|
|
||||||
private object InvokeControllerMethodWithRouteValues(MethodInfo method, object controllerInstance, Dictionary<string, string> routeValues)
|
|
||||||
{
|
|
||||||
object[] parameters = GetMethodParameters(method, routeValues);
|
|
||||||
return InvokeMethod(method, controllerInstance, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static readonly Dictionary<MethodInfo, ParameterInfo[]> methodParameterCache = new Dictionary<MethodInfo, ParameterInfo[]>();
|
|
||||||
/// <summary>
|
|
||||||
/// POST请求的调用控制器方法
|
|
||||||
/// </summary>
|
|
||||||
public object InvokeControllerMethod(MethodInfo method, object controllerInstance, dynamic requestData, Dictionary<string, string> routeValues)
|
|
||||||
{
|
|
||||||
object[] cachedMethodParameters;
|
|
||||||
|
|
||||||
if (!methodParameterCache.TryGetValue(method, out ParameterInfo[] parameters))
|
|
||||||
{
|
|
||||||
parameters = method.GetParameters();
|
|
||||||
}
|
|
||||||
|
|
||||||
cachedMethodParameters = new object[parameters.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < parameters.Length; i++)
|
|
||||||
{
|
|
||||||
string paramName = parameters[i].Name;
|
|
||||||
bool isUrlData = parameters[i].GetCustomAttribute(typeof(IsUrlDataAttribute)) != null;
|
|
||||||
bool isBobyData = parameters[i].GetCustomAttribute(typeof(IsBobyDataAttribute)) != null;
|
|
||||||
|
|
||||||
if (isUrlData)
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(paramName) && routeValues.TryGetValue(paramName, out string value))
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = ConvertValue(value, parameters[i].ParameterType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (isBobyData)
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = ConvertValue(requestData.ToString(), parameters[i].ParameterType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (requestData.ContainsKey(paramName))
|
|
||||||
{
|
|
||||||
if (parameters[i].ParameterType == typeof(string))
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = requestData[paramName].ToString();
|
|
||||||
}
|
|
||||||
else if (parameters[i].ParameterType == typeof(bool))
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = requestData[paramName?.ToLower()].ToBool();
|
|
||||||
}
|
|
||||||
else if (parameters[i].ParameterType == typeof(int))
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = requestData[paramName].ToInt();
|
|
||||||
}
|
|
||||||
else if (parameters[i].ParameterType == typeof(double))
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = requestData[paramName].ToDouble();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = ConvertValue(requestData[paramName], parameters[i].ParameterType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
cachedMethodParameters[i] = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 缓存方法和参数的映射
|
|
||||||
//methodParameterCache[method] = cachedMethodParameters;
|
|
||||||
|
|
||||||
|
|
||||||
// 调用方法
|
|
||||||
|
|
||||||
return method.Invoke(controllerInstance, cachedMethodParameters);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 检查方法入参参数类型,返回对应的入参数组
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method"></param>
|
|
||||||
/// <param name="routeValues"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private object[] GetMethodParameters(MethodInfo method, Dictionary<string, string> routeValues)
|
|
||||||
{
|
|
||||||
ParameterInfo[] methodParameters = method.GetParameters();
|
|
||||||
object[] parameters = new object[methodParameters.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < methodParameters.Length; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
string paramName = methodParameters[i].Name;
|
|
||||||
|
|
||||||
|
|
||||||
if (routeValues.TryGetValue(paramName, out string value))
|
|
||||||
{
|
|
||||||
parameters[i] = ConvertValue(value, methodParameters[i].ParameterType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*/// <summary>
|
|
||||||
/// 转为对应的类型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="targetType"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private object ConvertValue(object value, Type targetType)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
|
||||||
}
|
|
||||||
catch (JsonReaderException ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException ex)
|
|
||||||
{
|
|
||||||
// 如果无法转为对应的JSON对象
|
|
||||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
|
||||||
int endIndex = ex.Message.IndexOf("'", startIndex); // 查找类型信息结束的索引
|
|
||||||
var typeInfo = ex.Message.Substring(startIndex, endIndex - startIndex); // 提取出错类型信息,该怎么传出去?
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch // (Exception ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
/// <summary>
|
|
||||||
/// 转为对应的类型
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="targetType"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private object ConvertValue(string value, Type targetType)
|
|
||||||
{
|
|
||||||
if(targetType == typeof(string))
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject(value.ToString(), targetType);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (JsonReaderException ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException ex)
|
|
||||||
{
|
|
||||||
// 如果无法转为对应的JSON对象
|
|
||||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
|
||||||
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
|
||||||
var typeInfo = ex.Message.Substring(startIndex, endIndex); // 提取出错类型信息,该怎么传出去?
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
catch // (Exception ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 调用控制器方法传入参数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="method">方法</param>
|
|
||||||
/// <param name="controllerInstance">控制器实例</param>
|
|
||||||
/// <param name="methodParameters">参数列表</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static object InvokeMethod(MethodInfo method, object controllerInstance, object[] methodParameters)
|
|
||||||
{
|
|
||||||
|
|
||||||
object result = null;
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
result = method?.Invoke(controllerInstance, methodParameters);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (ArgumentException ex)
|
|
||||||
{
|
|
||||||
string targetType = ExtractTargetTypeFromExceptionMessage(ex.Message);
|
|
||||||
|
|
||||||
// 如果方法调用失败
|
|
||||||
result = new
|
|
||||||
{
|
|
||||||
error = $"函数签名类型[{targetType}]不符合",
|
|
||||||
};
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException ex)
|
|
||||||
{
|
|
||||||
|
|
||||||
// 查找类型信息开始的索引
|
|
||||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length;
|
|
||||||
// 查找类型信息结束的索引
|
|
||||||
int endIndex = ex.Message.IndexOf('\'');
|
|
||||||
// 提取类型信息
|
|
||||||
string typeInfo = ex.Message.Substring(startIndex, endIndex);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.ToString());
|
|
||||||
}
|
|
||||||
|
|
||||||
return result; // 调用方法并返回结果
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法声明,用于解析 URL 获取路由参数
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="uri"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static Dictionary<string, string> GetUrlData(Uri uri)
|
|
||||||
{
|
|
||||||
Dictionary<string, string> routeValues = new Dictionary<string, string>();
|
|
||||||
|
|
||||||
var pathParts = uri.ToString().Split('?'); // 拆分 URL,获取路径部分
|
|
||||||
|
|
||||||
if (pathParts.Length > 1) // 如果包含查询字符串
|
|
||||||
{
|
|
||||||
var queryParams = HttpUtility.ParseQueryString(pathParts[1]); // 解析查询字符串
|
|
||||||
|
|
||||||
foreach (string key in queryParams) // 遍历查询字符串的键值对
|
|
||||||
{
|
|
||||||
if (key == null) continue;
|
|
||||||
|
|
||||||
routeValues[key] = queryParams[key]; // 将键值对添加到路由参数字典中
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return routeValues; // 返回路由参数字典
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 读取Body中的消息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="request"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static async Task<string> ReadRequestBodyAsync(HttpListenerRequest request)
|
|
||||||
{
|
|
||||||
using (Stream stream = request.InputStream)
|
|
||||||
using (StreamReader reader = new StreamReader(stream, Encoding.UTF8))
|
|
||||||
{
|
|
||||||
return await reader.ReadToEndAsync();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 返回响应消息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="response"></param>
|
|
||||||
/// <param name="msg"></param>
|
|
||||||
private static void Return(HttpListenerResponse response, dynamic msg)
|
|
||||||
{
|
|
||||||
string resultData;
|
|
||||||
if (response != null)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (msg is IEnumerable && !(msg is string))
|
|
||||||
{
|
|
||||||
// If msg is a collection (e.g., array or list), serialize it as JArray
|
|
||||||
resultData = JArray.FromObject(msg).ToString();
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Otherwise, serialize it as JObject
|
|
||||||
resultData = JObject.FromObject(msg).ToString();
|
|
||||||
}
|
|
||||||
byte[] buffer = Encoding.UTF8.GetBytes(resultData);
|
|
||||||
response.ContentLength64 = buffer.Length;
|
|
||||||
response.OutputStream.Write(buffer, 0, buffer.Length);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
// If serialization fails, use the original message's string representation
|
|
||||||
resultData = msg.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 解析JSON
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="requestBody"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="Exception"></exception>
|
|
||||||
private static dynamic ParseJson(string requestBody)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (string.IsNullOrWhiteSpace(requestBody))
|
|
||||||
{
|
|
||||||
throw new Exception("Invalid JSON format");
|
|
||||||
}
|
|
||||||
return JObject.Parse(requestBody);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw new Exception("Invalid JSON format");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 修正方法特性中的URL格式
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="url"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static string CleanUrl(string url)
|
|
||||||
{
|
|
||||||
|
|
||||||
while (url.Length > 0 && url[0] == '/') // 去除开头的斜杠
|
|
||||||
{
|
|
||||||
url = url.Substring(1,url.Length-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
while (url.Length > 0 && url[url.Length-1] == '/') // 去除末尾的斜杠
|
|
||||||
{
|
|
||||||
url = url.Substring(0,url.Length-1);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < url.Length - 1; i++) // 去除连续的斜杠
|
|
||||||
{
|
|
||||||
if (url[i] == '/' && url[i + 1] == '/')
|
|
||||||
{
|
|
||||||
url = url.Remove(i, 1);
|
|
||||||
i--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return url; // 返回清理后的 URL
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 从控制器调用方法的异常中获取出出错类型的信息
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="errorMessage"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ExtractTargetTypeFromExceptionMessage(string errorMessage)
|
|
||||||
{
|
|
||||||
string targetText = "为类型“";
|
|
||||||
int startIndex = errorMessage.IndexOf(targetText);
|
|
||||||
if (startIndex != -1)
|
|
||||||
{
|
|
||||||
startIndex += targetText.Length;
|
|
||||||
int endIndex = errorMessage.IndexOf('\'');
|
|
||||||
if (endIndex != -1)
|
|
||||||
{
|
|
||||||
return errorMessage.Substring(startIndex, endIndex);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,196 +0,0 @@
|
|||||||
using Serein.Library.Api.Api;
|
|
||||||
using Serein.Library.Framework.IOC;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Net;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.Http
|
|
||||||
{
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// HTTP接口监听类
|
|
||||||
/// </summary>
|
|
||||||
public class WebServer
|
|
||||||
{
|
|
||||||
private readonly HttpListener listener; // HTTP 监听器
|
|
||||||
private Router router; // 路由器
|
|
||||||
private readonly RequestLimiter requestLimiter; //接口防刷
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public WebServer()
|
|
||||||
|
|
||||||
{
|
|
||||||
listener = new HttpListener();
|
|
||||||
|
|
||||||
requestLimiter = new RequestLimiter(5, 8);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
// 启动服务器
|
|
||||||
public WebServer Start(string prefixe, ISereinIoc serviceContainer)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
router = new Router(serviceContainer);
|
|
||||||
if (listener.IsListening)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!prefixe.Substring(prefixe.Length - 1, 1).Equals(@"/"))
|
|
||||||
{
|
|
||||||
prefixe += @"/";
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
listener.Prefixes.Add(prefixe); // 添加监听前缀
|
|
||||||
listener.Start(); // 开始监听
|
|
||||||
|
|
||||||
Console.WriteLine($"开始监听:{prefixe}");
|
|
||||||
Task.Run(async () =>
|
|
||||||
{
|
|
||||||
while (listener.IsListening)
|
|
||||||
{
|
|
||||||
var context = await listener.GetContextAsync(); // 获取请求上下文
|
|
||||||
_ = Task.Run(() => ProcessRequestAsync(context)); // 处理请求)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
catch (HttpListenerException ex) when (ex.ErrorCode == 183)
|
|
||||||
{
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 处理请求
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private async Task ProcessRequestAsync(HttpListenerContext context)
|
|
||||||
{
|
|
||||||
// 添加CORS头部
|
|
||||||
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
|
|
||||||
context.Response.Headers.Add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");
|
|
||||||
context.Response.Headers.Add("Access-Control-Allow-Headers", "Content-Type");
|
|
||||||
|
|
||||||
// 处理OPTIONS预检请求
|
|
||||||
if (context.Request.HttpMethod == "OPTIONS")
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
||||||
context.Response.Close();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var isPass = await router.RouteAsync(context); // 路由解析
|
|
||||||
if (isPass)
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.OK;
|
|
||||||
context.Response.Close(); // 关闭响应
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
context.Response.StatusCode = (int)HttpStatusCode.NotFound;
|
|
||||||
context.Response.Close(); // 关闭响应
|
|
||||||
}
|
|
||||||
|
|
||||||
//var isPass = requestLimiter.AllowRequest(context.Request);
|
|
||||||
//if (isPass)
|
|
||||||
//{
|
|
||||||
// // 如果路由没有匹配,返回 404
|
|
||||||
// router.RouteAsync(context); // 路由解析
|
|
||||||
//}
|
|
||||||
//else
|
|
||||||
//{
|
|
||||||
// context.Response.StatusCode = (int)HttpStatusCode.NotFound; // 返回 404 错误
|
|
||||||
// context.Response.Close(); // 关闭响应
|
|
||||||
//}
|
|
||||||
|
|
||||||
// var request = context.Request;
|
|
||||||
// 获取远程终结点信息
|
|
||||||
//var remoteEndPoint = context.Request.RemoteEndPoint;
|
|
||||||
//// 获取用户的IP地址和端口
|
|
||||||
//IPAddress ipAddress = remoteEndPoint.Address;
|
|
||||||
//int port = remoteEndPoint.Port;
|
|
||||||
//Console.WriteLine("外部连接:" + ipAddress.ToString() + ":" + port);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 停止服务器
|
|
||||||
public void Stop()
|
|
||||||
{
|
|
||||||
if (listener.IsListening)
|
|
||||||
{
|
|
||||||
listener?.Stop(); // 停止监听
|
|
||||||
listener?.Close(); // 关闭监听器
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterAutoController<T>()
|
|
||||||
{
|
|
||||||
//var instance = Activator.CreateInstance(typeof(T));
|
|
||||||
router.RegisterAutoController<T>();
|
|
||||||
}
|
|
||||||
|
|
||||||
/*public void RegisterRoute<T>(T controllerInstance)
|
|
||||||
{
|
|
||||||
router.RegisterRoute(controllerInstance);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 判断访问接口的频次是否正常
|
|
||||||
/// </summary>
|
|
||||||
public class RequestLimiter
|
|
||||||
{
|
|
||||||
public RequestLimiter(int seconds, int maxRequests)
|
|
||||||
{
|
|
||||||
interval = TimeSpan.FromSeconds(seconds);
|
|
||||||
maxRequests = maxRequests;
|
|
||||||
}
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, Queue<DateTime>> requestHistory = new ConcurrentDictionary<string, Queue<DateTime>>();
|
|
||||||
private readonly TimeSpan interval;
|
|
||||||
private readonly int maxRequests;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 判断访问接口的频次是否正常
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool AllowRequest(HttpListenerRequest request)
|
|
||||||
{
|
|
||||||
var clientIp = request.RemoteEndPoint.Address.ToString();
|
|
||||||
var clientPort = request.RemoteEndPoint.Port;
|
|
||||||
var clientKey = clientIp + ":" + clientPort;
|
|
||||||
|
|
||||||
var now = DateTime.Now;
|
|
||||||
|
|
||||||
// 尝试从字典中获取请求队列,不存在则创建新的队列
|
|
||||||
var requests = requestHistory.GetOrAdd(clientKey, new Queue<DateTime>());
|
|
||||||
|
|
||||||
lock (requests)
|
|
||||||
{
|
|
||||||
// 移除超出时间间隔的请求记录
|
|
||||||
while (requests.Count > 0 && now - requests.Peek() > interval)
|
|
||||||
{
|
|
||||||
requests.Dequeue();
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果请求数超过限制,拒绝请求
|
|
||||||
if (requests.Count >= maxRequests)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 添加当前请求时间,并允许请求
|
|
||||||
requests.Enqueue(now);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,378 +0,0 @@
|
|||||||
using Serein.Library.Api;
|
|
||||||
using Serein.Library.Attributes;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.IOC
|
|
||||||
{
|
|
||||||
|
|
||||||
public class SereinIoc : ISereinIoc
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly ConcurrentDictionary<string, object> _dependencies;
|
|
||||||
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
|
||||||
private readonly List<Type> _waitingForInstantiation;
|
|
||||||
|
|
||||||
public SereinIoc()
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies = new ConcurrentDictionary<string, object>
|
|
||||||
{
|
|
||||||
[typeof(ISereinIoc).FullName] = this
|
|
||||||
};
|
|
||||||
|
|
||||||
_typeMappings = new ConcurrentDictionary<string, Type>();
|
|
||||||
_waitingForInstantiation = new List<Type>();
|
|
||||||
}
|
|
||||||
public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
|
||||||
{
|
|
||||||
Register(type);
|
|
||||||
object instance;
|
|
||||||
|
|
||||||
if (_dependencies.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
instance = _dependencies[type.FullName];
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
instance = Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
|
|
||||||
_dependencies[type.FullName] = instance;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
|
|
||||||
}
|
|
||||||
public T CreateServiceInstance<T>(params object[] parameters)
|
|
||||||
{
|
|
||||||
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Reset()
|
|
||||||
{
|
|
||||||
foreach(var instancei in _dependencies.Values)
|
|
||||||
{
|
|
||||||
if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
|
||||||
{
|
|
||||||
disposable.Dispose();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
_dependencies.Clear();
|
|
||||||
_waitingForInstantiation.Clear();
|
|
||||||
//_typeMappings.Clear();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Register(Type type, params object[] parameters)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (!_typeMappings.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
_typeMappings[type.FullName] = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public ISereinIoc Register<T>(params object[] parameters)
|
|
||||||
{
|
|
||||||
Register(typeof(T), parameters);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
|
||||||
where TImplementation : TService
|
|
||||||
{
|
|
||||||
_typeMappings[typeof(TService).FullName] = typeof(TImplementation);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object GetOrInstantiate(Type type)
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
|
||||||
{
|
|
||||||
Register(type);
|
|
||||||
|
|
||||||
value = Instantiate(type);
|
|
||||||
|
|
||||||
InjectDependencies(type);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return value;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public T GetOrInstantiate<T>()
|
|
||||||
{
|
|
||||||
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
|
||||||
{
|
|
||||||
Register<T>();
|
|
||||||
|
|
||||||
value = Instantiate(typeof(T));
|
|
||||||
}
|
|
||||||
|
|
||||||
return (T)value;
|
|
||||||
//throw new InvalidOperationException("目标类型未创建实例");
|
|
||||||
}
|
|
||||||
public ISereinIoc Build()
|
|
||||||
{
|
|
||||||
foreach (var type in _typeMappings.Values)
|
|
||||||
{
|
|
||||||
|
|
||||||
if(!_dependencies.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var instance in _dependencies.Values)
|
|
||||||
{
|
|
||||||
|
|
||||||
InjectDependencies(instance); // 替换占位符
|
|
||||||
}
|
|
||||||
|
|
||||||
//var instance = Instantiate(item.Value);
|
|
||||||
|
|
||||||
TryInstantiateWaitingDependencies();
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Instantiate(Type controllerType, params object[] parameters)
|
|
||||||
{
|
|
||||||
var instance = Activator.CreateInstance(controllerType, parameters);
|
|
||||||
if(instance != null)
|
|
||||||
{
|
|
||||||
InjectDependencies(instance);
|
|
||||||
}
|
|
||||||
return instance;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void InjectDependencies(object instance)
|
|
||||||
{
|
|
||||||
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray()
|
|
||||||
.Where(p => p.CanWrite && p.GetCustomAttribute<AutoInjectionAttribute>() != null);
|
|
||||||
|
|
||||||
foreach (var property in properties)
|
|
||||||
{
|
|
||||||
var propertyType = property.PropertyType;
|
|
||||||
|
|
||||||
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
|
||||||
{
|
|
||||||
property.SetValue(instance, dependencyInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TryInstantiateWaitingDependencies()
|
|
||||||
{
|
|
||||||
foreach (var waitingType in _waitingForInstantiation.ToList())
|
|
||||||
{
|
|
||||||
if (_typeMappings.TryGetValue(waitingType.FullName, out var implementationType))
|
|
||||||
{
|
|
||||||
var instance = Instantiate(implementationType);
|
|
||||||
if (instance != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies[waitingType.FullName] = instance;
|
|
||||||
|
|
||||||
_waitingForInstantiation.Remove(waitingType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#region run()
|
|
||||||
public ISereinIoc Run<T>(Action<T> action)
|
|
||||||
{
|
|
||||||
var service = GetOrInstantiate<T>();
|
|
||||||
if (service != null)
|
|
||||||
{
|
|
||||||
action(service);
|
|
||||||
}
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2>(Action<T1, T2> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
|
|
||||||
action(service1, service2);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3>(Action<T1, T2, T3> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
action(service1, service2, service3);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4>(Action<T1, T2, T3, T4> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
action(service1, service2, service3, service4);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5>(Action<T1, T2, T3, T4, T5> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
action(service1, service2, service3, service4, service5);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5, T6>(Action<T1, T2, T3, T4, T5, T6> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
var service6 = GetOrInstantiate<T6>();
|
|
||||||
action(service1, service2, service3, service4, service5, service6);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7>(Action<T1, T2, T3, T4, T5, T6, T7> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
var service6 = GetOrInstantiate<T6>();
|
|
||||||
var service7 = GetOrInstantiate<T7>();
|
|
||||||
action(service1, service2, service3, service4, service5, service6, service7);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ISereinIoc Run<T1, T2, T3, T4, T5, T6, T7, T8>(Action<T1, T2, T3, T4, T5, T6, T7, T8> action)
|
|
||||||
{
|
|
||||||
var service1 = GetOrInstantiate<T1>();
|
|
||||||
var service2 = GetOrInstantiate<T2>();
|
|
||||||
var service3 = GetOrInstantiate<T3>();
|
|
||||||
var service4 = GetOrInstantiate<T4>();
|
|
||||||
var service5 = GetOrInstantiate<T5>();
|
|
||||||
var service6 = GetOrInstantiate<T6>();
|
|
||||||
var service7 = GetOrInstantiate<T7>();
|
|
||||||
var service8 = GetOrInstantiate<T8>();
|
|
||||||
action(service1, service2, service3, service4, service5, service6, service7, service8);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/* public interface IServiceContainer
|
|
||||||
{
|
|
||||||
ServiceContainer Register<T>(params object[] parameters);
|
|
||||||
ServiceContainer Register<TService, TImplementation>(params object[] parameters) where TImplementation : TService;
|
|
||||||
TService Resolve<TService>();
|
|
||||||
void Get<T>(Action<T> action);
|
|
||||||
object Instantiate(Type type, params object[] parameters);
|
|
||||||
|
|
||||||
}
|
|
||||||
public class ServiceContainer : IServiceContainer
|
|
||||||
{
|
|
||||||
private readonly Dictionary<Type, object> _dependencies;
|
|
||||||
public ServiceContainer()
|
|
||||||
{
|
|
||||||
_dependencies = new Dictionary<Type, object>
|
|
||||||
{
|
|
||||||
[typeof(IServiceContainer)] = this
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Get<T>(Action<T> action)
|
|
||||||
{
|
|
||||||
var service = Resolve<T>();
|
|
||||||
action(service);
|
|
||||||
}
|
|
||||||
public ServiceContainer Register<T>(params object[] parameters)
|
|
||||||
{
|
|
||||||
var instance = Instantiate(typeof(T), parameters);
|
|
||||||
_dependencies[typeof(T)] = instance;
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
public ServiceContainer Register<TService, TImplementation>(params object[] parameters)
|
|
||||||
where TImplementation : TService
|
|
||||||
{
|
|
||||||
|
|
||||||
_dependencies[typeof(TService)] = Instantiate(typeof(TImplementation), parameters);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public TService Resolve<TService>()
|
|
||||||
{
|
|
||||||
return (TService)_dependencies[typeof(TService)];
|
|
||||||
}
|
|
||||||
|
|
||||||
public object Instantiate(Type controllerType, params object[] parameters)
|
|
||||||
{
|
|
||||||
var constructors = controllerType.GetConstructors(); // 获取控制器的所有构造函数
|
|
||||||
|
|
||||||
// 查找具有最多参数的构造函数
|
|
||||||
var constructor = constructors.OrderByDescending(c => c.GetParameters().Length).FirstOrDefault();
|
|
||||||
|
|
||||||
if (constructor != null)
|
|
||||||
{
|
|
||||||
if (parameters.Length > 0)
|
|
||||||
{
|
|
||||||
return Activator.CreateInstance(controllerType, parameters);
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
var tmpParameters = constructor.GetParameters();
|
|
||||||
var dependencyInstances = new List<object>();
|
|
||||||
|
|
||||||
foreach (var parameter in tmpParameters)
|
|
||||||
{
|
|
||||||
var parameterType = parameter.ParameterType;
|
|
||||||
_dependencies.TryGetValue(parameterType, out var dependencyInstance);
|
|
||||||
dependencyInstances.Add(dependencyInstance);
|
|
||||||
if (dependencyInstance == null)
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// 用解析的依赖项实例化目标类型
|
|
||||||
return Activator.CreateInstance(controllerType, dependencyInstances.ToArray());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return Activator.CreateInstance(controllerType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.NodeFlow
|
|
||||||
{
|
|
||||||
public interface IDynamicFlowNode
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.LibraryFramework.NodeFlow
|
|
||||||
{
|
|
||||||
public enum DynamicNodeFrameworkType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化
|
|
||||||
/// </summary>
|
|
||||||
Init,
|
|
||||||
/// <summary>
|
|
||||||
/// 开始载入
|
|
||||||
/// </summary>
|
|
||||||
Loading,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束
|
|
||||||
/// </summary>
|
|
||||||
Exit,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器
|
|
||||||
/// </summary>
|
|
||||||
Flipflop,
|
|
||||||
/// <summary>
|
|
||||||
/// 条件节点
|
|
||||||
/// </summary>
|
|
||||||
Condition,
|
|
||||||
/// <summary>
|
|
||||||
/// 动作节点
|
|
||||||
/// </summary>
|
|
||||||
Action,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.NodeFlow
|
|
||||||
{
|
|
||||||
|
|
||||||
//public enum FlowStateType
|
|
||||||
//{
|
|
||||||
// /// <summary>
|
|
||||||
// /// 成功(方法成功执行)
|
|
||||||
// /// </summary>
|
|
||||||
// Succeed,
|
|
||||||
// /// <summary>
|
|
||||||
// /// 失败(方法没有成功执行,不过执行时没有发生非预期的错误)
|
|
||||||
// /// </summary>
|
|
||||||
// Fail,
|
|
||||||
// /// <summary>
|
|
||||||
// /// 异常(节点没有成功执行,执行时发生非预期的错误)
|
|
||||||
// /// </summary>
|
|
||||||
// Error,
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using Serein.Library.Api.Enums;
|
|
||||||
using System;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.NodeFlow.Tool
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,194 +0,0 @@
|
|||||||
namespace Serein.Library.Framework.NodeFlow.Tool
|
|
||||||
{
|
|
||||||
|
|
||||||
#region 锁、tsk工具 (已注释)
|
|
||||||
/*public class LockManager
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, LockQueue> _locks = new ConcurrentDictionary<string, LockQueue>();
|
|
||||||
|
|
||||||
public void CreateLock(string name)
|
|
||||||
{
|
|
||||||
_locks.TryAdd(name, new LockQueue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!_locks.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockQueue = _locks[name];
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
lockQueue.Queue.Enqueue(tcs);
|
|
||||||
if (lockQueue.Queue.Count == 1)
|
|
||||||
{
|
|
||||||
tcs.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// 处理取消操作
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
cancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
if (lockQueue.Queue.Contains(tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReleaseLock(string name)
|
|
||||||
{
|
|
||||||
if (!_locks.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockQueue = _locks[name];
|
|
||||||
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
if (lockQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
lockQueue.Queue.Dequeue();
|
|
||||||
|
|
||||||
if (lockQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
var next = lockQueue.Queue.Peek();
|
|
||||||
next.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LockQueue
|
|
||||||
{
|
|
||||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface ITaskResult
|
|
||||||
{
|
|
||||||
object Result { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TaskResult<T> : ITaskResult
|
|
||||||
{
|
|
||||||
public TaskResult(T result)
|
|
||||||
{
|
|
||||||
Result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Result { get; }
|
|
||||||
|
|
||||||
object ITaskResult.Result => Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DynamicTasks
|
|
||||||
{
|
|
||||||
private static readonly ConcurrentDictionary<string, Task<ITaskResult>> TaskGuidPairs = new();
|
|
||||||
public static Task<ITaskResult> GetTask(string Guid)
|
|
||||||
{
|
|
||||||
TaskGuidPairs.TryGetValue(Guid, out Task<ITaskResult> task);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool AddTask<T>(string Guid, T result)
|
|
||||||
{
|
|
||||||
var task = Task.FromResult<ITaskResult>(new TaskResult<T>(result));
|
|
||||||
|
|
||||||
return TaskGuidPairs.TryAdd(Guid, task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class TaskNodeManager
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, TaskQueue> _taskQueues = new ConcurrentDictionary<string, TaskQueue>();
|
|
||||||
|
|
||||||
public void CreateTaskNode(string name)
|
|
||||||
{
|
|
||||||
_taskQueues.TryAdd(name, new TaskQueue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!_taskQueues.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskQueue = _taskQueues[name];
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
taskQueue.Queue.Enqueue(tcs);
|
|
||||||
if (taskQueue.Queue.Count == 1)
|
|
||||||
{
|
|
||||||
tcs.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// 处理取消操作
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
cancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
if (taskQueue.Queue.Contains(tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CompleteTaskNode(string name)
|
|
||||||
{
|
|
||||||
if (!_taskQueues.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskQueue = _taskQueues[name];
|
|
||||||
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
if (taskQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
taskQueue.Queue.Dequeue();
|
|
||||||
|
|
||||||
if (taskQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
var next = taskQueue.Queue.Peek();
|
|
||||||
next.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TaskQueue
|
|
||||||
{
|
|
||||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
using Serein.Library.Enums;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Library.Framework.NodeFlow.Tool
|
|
||||||
{
|
|
||||||
public class TcsSignalException : Exception
|
|
||||||
{
|
|
||||||
public FlowStateType FsState { get; set; }
|
|
||||||
public TcsSignalException(string message) : base(message)
|
|
||||||
{
|
|
||||||
FsState = FlowStateType.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TcsSignal<TSignal> where TSignal : struct, Enum
|
|
||||||
{
|
|
||||||
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
|
||||||
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new ConcurrentDictionary<TSignal, TaskCompletionSource<object>>();
|
|
||||||
|
|
||||||
public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new ConcurrentDictionary<TSignal, object>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发信号
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="signal">信号</param>
|
|
||||||
/// <param name="value">传递的参数</param>
|
|
||||||
/// <returns>是否成功触发</returns>
|
|
||||||
public bool TriggerSignal<T>(TSignal signal, T value)
|
|
||||||
{
|
|
||||||
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
|
||||||
lock (tcsLock)
|
|
||||||
{
|
|
||||||
if (TcsEvent.TryRemove(signal, out var waitTcs))
|
|
||||||
{
|
|
||||||
waitTcs.SetResult(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
|
||||||
{
|
|
||||||
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
|
||||||
lock (tcsLock)
|
|
||||||
{
|
|
||||||
var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource<object>());
|
|
||||||
return tcs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CancelTask()
|
|
||||||
{
|
|
||||||
foreach (var tcs in TcsEvent.Values)
|
|
||||||
{
|
|
||||||
tcs.SetException(new TcsSignalException("任务取消"));
|
|
||||||
}
|
|
||||||
TcsEvent.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,186 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Newtonsoft.Json.Converters;
|
|
||||||
using System;
|
|
||||||
using System.Collections;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.IO;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Tool
|
|
||||||
{
|
|
||||||
public static class DataHelper
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 把Object转换为Json字符串
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="obj"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static string ToJson(this object obj)
|
|
||||||
{
|
|
||||||
IsoDateTimeConverter val = new IsoDateTimeConverter();
|
|
||||||
val.DateTimeFormat = "yyyy-MM-dd HH:mm:ss";
|
|
||||||
IsoDateTimeConverter val2 = val;
|
|
||||||
return JsonConvert.SerializeObject(obj, (JsonConverter[])(object)new JsonConverter[1] { (JsonConverter)val2 });
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 把Json文本转为实体
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="input"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static T FromJSON<T>(this string input)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (typeof(T).IsAssignableFrom(typeof(T)))
|
|
||||||
{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
return JsonConvert.DeserializeObject<T>(input);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex.Message);
|
|
||||||
// return default(T);
|
|
||||||
|
|
||||||
return default;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static List<T> IListToList<T>(IList list)
|
|
||||||
{
|
|
||||||
T[] array = new T[list.Count];
|
|
||||||
list.CopyTo(array, 0);
|
|
||||||
return new List<T>(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataTable GetNewDataTable(DataTable dt, string condition)
|
|
||||||
{
|
|
||||||
if (!IsExistRows(dt))
|
|
||||||
{
|
|
||||||
if (condition.Trim() == "")
|
|
||||||
{
|
|
||||||
return dt;
|
|
||||||
}
|
|
||||||
|
|
||||||
DataTable dataTable = new DataTable();
|
|
||||||
dataTable = dt.Clone();
|
|
||||||
DataRow[] array = dt.Select(condition);
|
|
||||||
for (int i = 0; i < array.Length; i++)
|
|
||||||
{
|
|
||||||
dataTable.ImportRow(array[i]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool IsExistRows(DataTable dt)
|
|
||||||
{
|
|
||||||
if (dt != null && dt.Rows.Count > 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static Hashtable DataTableToHashtable(DataTable dt)
|
|
||||||
{
|
|
||||||
Hashtable hashtable = new Hashtable();
|
|
||||||
foreach (DataRow row in dt.Rows)
|
|
||||||
{
|
|
||||||
for (int i = 0; i < dt.Columns.Count; i++)
|
|
||||||
{
|
|
||||||
string columnName = dt.Columns[i].ColumnName;
|
|
||||||
hashtable[columnName] = row[columnName];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return hashtable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static DataTable ListToDataTable<T>(List<T> entitys)
|
|
||||||
{
|
|
||||||
if (entitys == null || entitys.Count < 1)
|
|
||||||
{
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
Type type = entitys[0].GetType();
|
|
||||||
|
|
||||||
PropertyInfo[] properties = type.GetProperties();
|
|
||||||
DataTable dataTable = new DataTable();
|
|
||||||
for (int i = 0; i < properties.Length; i++)
|
|
||||||
{
|
|
||||||
dataTable.Columns.Add(properties[i].Name);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (T entity in entitys)
|
|
||||||
{
|
|
||||||
|
|
||||||
object obj = entity;
|
|
||||||
|
|
||||||
|
|
||||||
if (obj.GetType() != type)
|
|
||||||
{
|
|
||||||
throw new Exception("要转换的集合元素类型不一致");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
object[] array = new object[properties.Length];
|
|
||||||
for (int j = 0; j < properties.Length; j++)
|
|
||||||
{
|
|
||||||
|
|
||||||
array[j] = properties[j].GetValue(obj, null);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
dataTable.Rows.Add(array);
|
|
||||||
}
|
|
||||||
|
|
||||||
return dataTable;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string DataTableToXML(DataTable dt)
|
|
||||||
{
|
|
||||||
if (dt != null && dt.Rows.Count > 0)
|
|
||||||
{
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
|
||||||
dt.WriteXml((TextWriter)stringWriter);
|
|
||||||
return stringWriter.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static string DataSetToXML(DataSet ds)
|
|
||||||
{
|
|
||||||
if (ds != null)
|
|
||||||
{
|
|
||||||
StringWriter stringWriter = new StringWriter();
|
|
||||||
ds.WriteXml((TextWriter)stringWriter);
|
|
||||||
return stringWriter.ToString();
|
|
||||||
}
|
|
||||||
|
|
||||||
return string.Empty;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,19 +10,12 @@ namespace Serein.Library.Api
|
|||||||
/// 获取或创建类型的实例(不注入依赖项)
|
/// 获取或创建类型的实例(不注入依赖项)
|
||||||
/// </summary>
|
/// </summary>
|
||||||
object GetOrCreateServiceInstance(Type serviceType, params object[] parameters);
|
object GetOrCreateServiceInstance(Type serviceType, params object[] parameters);
|
||||||
T CreateServiceInstance<T>(params object[] parameters);
|
T GetOrCreateServiceInstance<T>(params object[] parameters);
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 清空
|
/// 清空
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
ISereinIoc Reset();
|
ISereinIoc Reset();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 以已存在的实例对象注册
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="instantiate"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
ISereinIoc RegisterInstantiate(object instantiate);
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 注册实例
|
/// 注册实例
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,83 +0,0 @@
|
|||||||
using Serein.Library.Entity;
|
|
||||||
using Serein.Library.Enums;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
|
|
||||||
namespace Serein.Library.Base
|
|
||||||
{
|
|
||||||
public abstract class NodeBase
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 节点类型
|
|
||||||
/// </summary>
|
|
||||||
public abstract NodeControlType ControlType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法描述,对应DLL的方法
|
|
||||||
/// </summary>
|
|
||||||
public abstract MethodDetails MethodDetails { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 节点guid
|
|
||||||
/// </summary>
|
|
||||||
public abstract string Guid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显示名称
|
|
||||||
/// </summary>
|
|
||||||
public abstract string DisplayName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为起点控件
|
|
||||||
/// </summary>
|
|
||||||
public abstract bool IsStart { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行时的上一节点
|
|
||||||
/// </summary>
|
|
||||||
public abstract NodeBase PreviousNode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上一节点集合
|
|
||||||
/// </summary>
|
|
||||||
public abstract List<NodeBase> PreviousNodes { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下一节点集合(真分支)
|
|
||||||
/// </summary>
|
|
||||||
public abstract List<NodeBase> SucceedBranch { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下一节点集合(假分支)
|
|
||||||
/// </summary>
|
|
||||||
public abstract List<NodeBase> FailBranch { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异常分支
|
|
||||||
/// </summary>
|
|
||||||
public abstract List<NodeBase> ErrorBranch { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上游分支
|
|
||||||
/// </summary>
|
|
||||||
public abstract List<NodeBase> UpstreamBranch { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定)
|
|
||||||
/// </summary>
|
|
||||||
public abstract FlowStateType FlowState { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
|
||||||
/// </summary>
|
|
||||||
public abstract Exception RuningException { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前传递数据(执行了节点对应的方法,才会存在值)
|
|
||||||
/// </summary>
|
|
||||||
public abstract object FlowData { get; set; }
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,110 +0,0 @@
|
|||||||
using Serein.Library.Api;
|
|
||||||
using Serein.Library.Entity;
|
|
||||||
using Serein.Library.Enums;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
|
|
||||||
namespace Serein.Library.Base
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
|
||||||
/// </summary>
|
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
|
||||||
{
|
|
||||||
public NodeControlType ControlType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法描述,对应DLL的方法
|
|
||||||
/// </summary>
|
|
||||||
public MethodDetails MethodDetails { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 节点guid
|
|
||||||
/// </summary>
|
|
||||||
public string Guid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显示名称
|
|
||||||
/// </summary>
|
|
||||||
public string DisplayName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为起点控件
|
|
||||||
/// </summary>
|
|
||||||
public bool IsStart { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行时的上一节点
|
|
||||||
/// </summary>
|
|
||||||
public NodeModelBase PreviousNode { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上一节点集合
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeModelBase> PreviousNodes { get; set; } = new List<NodeModelBase>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下一节点集合(真分支)
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeModelBase> SucceedBranch { get; set; } = new List<NodeModelBase>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 下一节点集合(假分支)
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeModelBase> FailBranch { get; set; } = new List<NodeModelBase>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 异常分支
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeModelBase> ErrorBranch { get; set; } = new List<NodeModelBase>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 上游分支
|
|
||||||
/// </summary>
|
|
||||||
public List<NodeModelBase> UpstreamBranch { get; set; } = new List<NodeModelBase>();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前执行状态(进入真分支还是假分支,异常分支在异常中确定)
|
|
||||||
/// </summary>
|
|
||||||
public FlowStateType FlowState { get; set; } = FlowStateType.None;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
|
||||||
/// </summary>
|
|
||||||
public Exception RuningException { get; set; } = null;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 当前传递数据(执行了节点对应的方法,才会存在值)
|
|
||||||
/// </summary>
|
|
||||||
public object FlowData { get; set; } = null;
|
|
||||||
|
|
||||||
public abstract Parameterdata[] GetParameterdatas();
|
|
||||||
public virtual NodeInfo ToInfo()
|
|
||||||
{
|
|
||||||
if (MethodDetails == null) return null;
|
|
||||||
|
|
||||||
var trueNodes = SucceedBranch.Select(item => item.Guid); // 真分支
|
|
||||||
var falseNodes = FailBranch.Select(item => item.Guid);// 假分支
|
|
||||||
var upstreamNodes = UpstreamBranch.Select(item => item.Guid);// 上游分支
|
|
||||||
var errorNodes = ErrorBranch.Select(item => item.Guid);// 异常分支
|
|
||||||
|
|
||||||
// 生成参数列表
|
|
||||||
Parameterdata[] parameterData = GetParameterdatas();
|
|
||||||
|
|
||||||
return new NodeInfo
|
|
||||||
{
|
|
||||||
Guid = Guid,
|
|
||||||
MethodName = MethodDetails?.MethodName,
|
|
||||||
Label = DisplayName ?? "",
|
|
||||||
Type = this.GetType().ToString(),
|
|
||||||
TrueNodes = trueNodes.ToArray(),
|
|
||||||
FalseNodes = falseNodes.ToArray(),
|
|
||||||
UpstreamNodes = upstreamNodes.ToArray(),
|
|
||||||
ParameterData = parameterData.ToArray(),
|
|
||||||
ErrorNodes = errorNodes.ToArray(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,480 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Serein.Library.Api;
|
|
||||||
using Serein.Library.Entity;
|
|
||||||
using Serein.Library.Enums;
|
|
||||||
using Serein.Library.Utils;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Threading;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Library.Base
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
|
||||||
/// </summary>
|
|
||||||
public abstract partial class NodeModelBase : IDynamicFlowNode
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 执行节点对应的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">流程上下文</param>
|
|
||||||
/// <returns>节点传回数据对象</returns>
|
|
||||||
public virtual object Execute(IDynamicContext context)
|
|
||||||
{
|
|
||||||
MethodDetails md = MethodDetails;
|
|
||||||
object result = null;
|
|
||||||
var del = md.MethodDelegate;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (md.ExplicitDatas.Length == 0)
|
|
||||||
{
|
|
||||||
if (md.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
object[] parameters = GetParameters(context, MethodDetails);
|
|
||||||
if (md.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
FlowState = FlowStateType.Error;
|
|
||||||
RuningException = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行等待触发器的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns>节点传回数据对象</returns>
|
|
||||||
/// <exception cref="RuningException"></exception>
|
|
||||||
public virtual async Task<object> ExecuteAsync(IDynamicContext context)
|
|
||||||
{
|
|
||||||
MethodDetails md = MethodDetails;
|
|
||||||
object result = null;
|
|
||||||
|
|
||||||
IFlipflopContext flipflopContext = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 调用委托并获取结果
|
|
||||||
if (md.ExplicitDatas.Length == 0)
|
|
||||||
{
|
|
||||||
flipflopContext = await ((Func<object, Task<IFlipflopContext>>)md.MethodDelegate).Invoke(MethodDetails.ActingInstance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
object[] parameters = GetParameters(context, MethodDetails);
|
|
||||||
flipflopContext = await ((Func<object, object[], Task<IFlipflopContext>>)md.MethodDelegate).Invoke(MethodDetails.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flipflopContext != null)
|
|
||||||
{
|
|
||||||
FlowState = flipflopContext.State;
|
|
||||||
if (flipflopContext.State == FlowStateType.Succeed)
|
|
||||||
{
|
|
||||||
result = flipflopContext.Data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
FlowState = FlowStateType.Error;
|
|
||||||
RuningException = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始执行
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public async Task StartExecution(IDynamicContext context)
|
|
||||||
{
|
|
||||||
var cts = context.SereinIoc.GetOrInstantiate<CancellationTokenSource>();
|
|
||||||
|
|
||||||
Stack<NodeModelBase> stack = new Stack<NodeModelBase>();
|
|
||||||
stack.Push(this);
|
|
||||||
|
|
||||||
while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环
|
|
||||||
{
|
|
||||||
// 从栈中弹出一个节点作为当前节点进行处理
|
|
||||||
var currentNode = stack.Pop();
|
|
||||||
|
|
||||||
// 设置方法执行的对象
|
|
||||||
if (currentNode.MethodDetails != null)
|
|
||||||
{
|
|
||||||
if(currentNode.MethodDetails.ActingInstance == null)
|
|
||||||
{
|
|
||||||
currentNode.MethodDetails.ActingInstance = context.SereinIoc.GetOrInstantiate(MethodDetails.ActingInstanceType);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取上游分支,首先执行一次
|
|
||||||
var upstreamNodes = currentNode.UpstreamBranch;
|
|
||||||
for (int i = upstreamNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
upstreamNodes[i].PreviousNode = currentNode;
|
|
||||||
await upstreamNodes[i].StartExecution(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == NodeType.Flipflop)
|
|
||||||
{
|
|
||||||
// 触发器节点
|
|
||||||
currentNode.FlowData = await currentNode.ExecuteAsync(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 动作节点
|
|
||||||
currentNode.FlowData = currentNode.Execute(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
List<NodeModelBase> nextNodes = null ;
|
|
||||||
switch (currentNode.FlowState)
|
|
||||||
{
|
|
||||||
case FlowStateType.Succeed:
|
|
||||||
nextNodes = currentNode.SucceedBranch;
|
|
||||||
break;
|
|
||||||
case FlowStateType.Fail :
|
|
||||||
nextNodes = currentNode.FailBranch;
|
|
||||||
break;
|
|
||||||
case FlowStateType.Error :
|
|
||||||
nextNodes = currentNode.ErrorBranch;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
if(nextNodes != null)
|
|
||||||
{
|
|
||||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
nextNodes[i].PreviousNode = currentNode;
|
|
||||||
stack.Push(nextNodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/*var nextNodes = currentNode.FlowState switch
|
|
||||||
{
|
|
||||||
FlowStateType.Succeed => currentNode.SucceedBranch,
|
|
||||||
FlowStateType.Fail => currentNode.FailBranch,
|
|
||||||
FlowStateType.Error => currentNode.ErrorBranch,
|
|
||||||
_ => throw new Exception("非预期的枚举值")
|
|
||||||
};*/
|
|
||||||
|
|
||||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对应的参数数组
|
|
||||||
/// </summary>
|
|
||||||
public object[] GetParameters(IDynamicContext context, MethodDetails md)
|
|
||||||
{
|
|
||||||
// 用正确的大小初始化参数数组
|
|
||||||
var types = md.ExplicitDatas.Select(it => it.DataType).ToArray();
|
|
||||||
if (types.Length == 0)
|
|
||||||
{
|
|
||||||
return new object[] { md.ActingInstance };
|
|
||||||
}
|
|
||||||
|
|
||||||
object[] parameters = new object[types.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < types.Length; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
var mdEd = md.ExplicitDatas[i];
|
|
||||||
Type type = mdEd.DataType;
|
|
||||||
|
|
||||||
var f1 = PreviousNode?.FlowData?.GetType();
|
|
||||||
var f2 = mdEd.DataType;
|
|
||||||
if (type == typeof(IDynamicContext))
|
|
||||||
{
|
|
||||||
parameters[i] = context;
|
|
||||||
}
|
|
||||||
else if (type == typeof(MethodDetails))
|
|
||||||
{
|
|
||||||
parameters[i] = md;
|
|
||||||
}
|
|
||||||
else if (type == typeof(NodeModelBase))
|
|
||||||
{
|
|
||||||
parameters[i] = this;
|
|
||||||
}
|
|
||||||
else if (mdEd.IsExplicitData) // 显式参数
|
|
||||||
{
|
|
||||||
// 判断是否使用表达式解析
|
|
||||||
if (mdEd.DataValue[0] == '@')
|
|
||||||
{
|
|
||||||
var expResult = SerinExpressionEvaluator.Evaluate(mdEd.DataValue, PreviousNode?.FlowData, out bool isChange);
|
|
||||||
|
|
||||||
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(string))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(string));
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(bool))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(bool));
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(int))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(int));
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(double))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(double));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = expResult;
|
|
||||||
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(string))
|
|
||||||
{
|
|
||||||
parameters[i] = mdEd.DataValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(bool))
|
|
||||||
{
|
|
||||||
parameters[i] = bool.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(int))
|
|
||||||
{
|
|
||||||
parameters[i] = int.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(double))
|
|
||||||
{
|
|
||||||
parameters[i] = double.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = "";
|
|
||||||
|
|
||||||
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (f1 != null && f2 != null)
|
|
||||||
{
|
|
||||||
if (f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName))
|
|
||||||
{
|
|
||||||
parameters[i] = PreviousNode?.FlowData;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
var tmpParameter = PreviousNode?.FlowData?.ToString();
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, tmpParameter);
|
|
||||||
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(string))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = tmpParameter;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(bool))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = bool.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(int))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = int.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(double))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = double.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tmpParameter != null && mdEd.DataType != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = ConvertValue(tmpParameter, mdEd.DataType);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// json文本反序列化为对象
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="targetType"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private dynamic ConvertValue(string value, Type targetType)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject(value, targetType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JsonReaderException ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException ex)
|
|
||||||
{
|
|
||||||
// 如果无法转为对应的JSON对象
|
|
||||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
|
||||||
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
|
||||||
var typeInfo = ex.Message.Substring(startIndex,endIndex); // 提取出错类型信息,该怎么传出去?
|
|
||||||
Console.WriteLine("无法转为对应的JSON对象:" + typeInfo);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch // (Exception ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 完整的ExecuteAsync调用方法(不要删除)
|
|
||||||
//public virtual async Task<object?> ExecuteAsync(DynamicContext context)
|
|
||||||
//{
|
|
||||||
// MethodDetails md = MethodDetails;
|
|
||||||
// object? result = null;
|
|
||||||
// if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
// {
|
|
||||||
// if (md.ExplicitDatas.Length == 0)
|
|
||||||
// {
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// else if (md.ReturnType == typeof(Task<FlipflopContext>))
|
|
||||||
// {
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// FlipflopContext flipflopContext = await ((Func<object, Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance);
|
|
||||||
|
|
||||||
// if (flipflopContext != null)
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Cancel)
|
|
||||||
// {
|
|
||||||
// throw new Exception("this async task is cancel.");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Succeed)
|
|
||||||
// {
|
|
||||||
// CurrentState = true;
|
|
||||||
// result = flipflopContext.Data;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// CurrentState = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// object?[]? parameters = GetParameters(context, MethodDetails);
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// else if (md.ReturnType == typeof(Task<FlipflopContext>))
|
|
||||||
// {
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// FlipflopContext flipflopContext = await ((Func<object, object[], Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
|
||||||
|
|
||||||
// if (flipflopContext != null)
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Cancel)
|
|
||||||
// {
|
|
||||||
// throw new Exception("取消此异步");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// CurrentState = flipflopContext.State == FfState.Succeed;
|
|
||||||
// result = flipflopContext.Data;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// context.SetFlowData(result);
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -10,18 +10,9 @@
|
|||||||
<None Remove="Utils\SerinExpression\**" />
|
<None Remove="Utils\SerinExpression\**" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Compile Remove="Base\NodeBase.cs" />
|
|
||||||
<Compile Remove="Base\NodeModelBaseData.cs" />
|
|
||||||
<Compile Remove="Base\NodeModelBaseFunc.cs" />
|
|
||||||
</ItemGroup>
|
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
|
||||||
</ItemGroup>
|
<PackageReference Include="System.ValueTuple" Version="4.5.0" />
|
||||||
|
|
||||||
<ItemGroup>
|
|
||||||
<Folder Include="Base\" />
|
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|||||||
@@ -8,55 +8,84 @@ using System.Reflection;
|
|||||||
|
|
||||||
namespace Serein.Library.Utils
|
namespace Serein.Library.Utils
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// IOC管理容器
|
||||||
|
/// </summary>
|
||||||
public class SereinIoc : ISereinIoc
|
public class SereinIoc : ISereinIoc
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 实例集合
|
||||||
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, object> _dependencies;
|
private readonly ConcurrentDictionary<string, object> _dependencies;
|
||||||
|
/// <summary>
|
||||||
|
/// 未完成注入的实例集合。
|
||||||
|
/// 键:需要的类型名称
|
||||||
|
/// 值:对象实例(存储对象)
|
||||||
|
/// </summary>
|
||||||
|
private readonly ConcurrentDictionary<string, List<(object,PropertyInfo)>> _unfinishedDependencies;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 类型集合
|
||||||
|
/// </summary>
|
||||||
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
private readonly ConcurrentDictionary<string, Type> _typeMappings;
|
||||||
|
/// <summary>
|
||||||
|
/// 待实例化的类型
|
||||||
|
/// </summary>
|
||||||
private readonly List<Type> _waitingForInstantiation;
|
private readonly List<Type> _waitingForInstantiation;
|
||||||
|
|
||||||
public SereinIoc()
|
public SereinIoc()
|
||||||
{
|
{
|
||||||
|
// 首先注册自己
|
||||||
_dependencies = new ConcurrentDictionary<string, object>
|
_dependencies = new ConcurrentDictionary<string, object>
|
||||||
{
|
{
|
||||||
[typeof(ISereinIoc).FullName] = this
|
[typeof(ISereinIoc).FullName] = this
|
||||||
};
|
};
|
||||||
|
_typeMappings = new ConcurrentDictionary<string, Type>
|
||||||
_typeMappings = new ConcurrentDictionary<string, Type>();
|
{
|
||||||
|
[typeof(ISereinIoc).FullName] = typeof(ISereinIoc)
|
||||||
|
};
|
||||||
|
_unfinishedDependencies = new ConcurrentDictionary<string, List<(object, PropertyInfo)>>();
|
||||||
_waitingForInstantiation = new List<Type>();
|
_waitingForInstantiation = new List<Type>();
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取或创建实例对象(不注入对象的依赖项)
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">目标类型</param>
|
||||||
|
/// <param name="parameters">构造函数的参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
public object GetOrCreateServiceInstance(Type type, params object[] parameters)
|
||||||
{
|
{
|
||||||
Register(type);
|
|
||||||
object instance;
|
|
||||||
|
|
||||||
if (_dependencies.ContainsKey(type.FullName))
|
if (_dependencies.ContainsKey(type.FullName))
|
||||||
{
|
{
|
||||||
instance = _dependencies[type.FullName];
|
return _dependencies[type.FullName];
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
var instance = Activator.CreateInstance(type); // 创建目标类型的实例对象
|
||||||
instance = Activator.CreateInstance(type);
|
InjectDependencies(instance);// 注入目标对象的依赖项
|
||||||
|
_dependencies[type.FullName] = instance; // 记录实例
|
||||||
|
return instance;
|
||||||
_dependencies[type.FullName] = instance;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
return instance;
|
|
||||||
|
|
||||||
}
|
}
|
||||||
public T CreateServiceInstance<T>(params object[] parameters)
|
|
||||||
|
/// <summary>
|
||||||
|
/// 泛型方法, 获取或创建实例对象(不注入对象的依赖项)
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T">目标类型</typeparam>
|
||||||
|
/// <param name="parameters">构造函数的参数</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
public T GetOrCreateServiceInstance<T>(params object[] parameters)
|
||||||
{
|
{
|
||||||
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
return (T)GetOrCreateServiceInstance(typeof(T), parameters);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 清空容器对象
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public ISereinIoc Reset()
|
public ISereinIoc Reset()
|
||||||
{
|
{
|
||||||
|
// 检查是否存在非托管资源
|
||||||
foreach(var instancei in _dependencies.Values)
|
foreach(var instancei in _dependencies.Values)
|
||||||
{
|
{
|
||||||
if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
if (typeof(IDisposable).IsAssignableFrom(instancei.GetType()) && instancei is IDisposable disposable)
|
||||||
@@ -64,54 +93,52 @@ namespace Serein.Library.Utils
|
|||||||
disposable.Dispose();
|
disposable.Dispose();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
_typeMappings.Clear();
|
||||||
_dependencies.Clear();
|
_dependencies.Clear();
|
||||||
_waitingForInstantiation.Clear();
|
_waitingForInstantiation.Clear();
|
||||||
//_typeMappings.Clear();
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public ISereinIoc RegisterInstantiate(object instantiate)
|
/// <summary>
|
||||||
{
|
/// 注册类型
|
||||||
//var type = instantiate.GetType();
|
/// </summary>
|
||||||
if (!_typeMappings.TryGetValue(instantiate.GetType().FullName,out var type))
|
/// <param name="type">目标类型</param>
|
||||||
{
|
/// <param name="parameters">参数</param>
|
||||||
_typeMappings[type.FullName] = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!_dependencies.TryGetValue(type.FullName, out var instancei))
|
|
||||||
{
|
|
||||||
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
|
||||||
}
|
|
||||||
// _dependencies.AddOrUpdate(type.FullName,s => instantiate, (s,o) => instantiate);
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
public ISereinIoc Register(Type type, params object[] parameters)
|
public ISereinIoc Register(Type type, params object[] parameters)
|
||||||
{
|
{
|
||||||
|
RegisterType(type.FullName, type);
|
||||||
if (!_typeMappings.ContainsKey(type.FullName))
|
|
||||||
{
|
|
||||||
_typeMappings[type.FullName] = type;
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 注册类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type">目标类型</param>
|
||||||
|
/// <param name="parameters">参数</param>
|
||||||
public ISereinIoc Register<T>(params object[] parameters)
|
public ISereinIoc Register<T>(params object[] parameters)
|
||||||
{
|
{
|
||||||
Register(typeof(T), parameters);
|
var type = typeof(T);
|
||||||
|
RegisterType(type.FullName, type);
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
public ISereinIoc Register<TService, TImplementation>(params object[] parameters)
|
||||||
where TImplementation : TService
|
where TImplementation : TService
|
||||||
{
|
{
|
||||||
_typeMappings[typeof(TService).FullName] = typeof(TImplementation);
|
var typeFullName = typeof(TService).FullName;
|
||||||
|
RegisterType(typeFullName, typeof(TImplementation));
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
/// <returns></returns>
|
||||||
public object GetOrInstantiate(Type type)
|
public object GetOrInstantiate(Type type)
|
||||||
{
|
{
|
||||||
|
|
||||||
|
|
||||||
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
if (!_dependencies.TryGetValue(type.FullName, out object value))
|
||||||
{
|
{
|
||||||
Register(type);
|
Register(type);
|
||||||
@@ -127,47 +154,57 @@ namespace Serein.Library.Utils
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public T GetOrInstantiate<T>()
|
public T GetOrInstantiate<T>()
|
||||||
{
|
{
|
||||||
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
if(!_dependencies.TryGetValue(typeof(T).FullName, out object value))
|
||||||
{
|
{
|
||||||
Register<T>();
|
Register(typeof(T));
|
||||||
|
|
||||||
value = Instantiate(typeof(T));
|
value = Instantiate(typeof(T));
|
||||||
}
|
}
|
||||||
|
|
||||||
return (T)value;
|
return (T)value;
|
||||||
//throw new InvalidOperationException("目标类型未创建实例");
|
//throw new InvalidOperationException("目标类型未创建实例");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 实例化所有已注册的类型,并尝试绑定
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
public ISereinIoc Build()
|
public ISereinIoc Build()
|
||||||
{
|
{
|
||||||
|
|
||||||
// 遍历已注册类型
|
// 遍历已注册类型
|
||||||
foreach (var type in _typeMappings.Values)
|
foreach (var type in _typeMappings.Values.ToArray())
|
||||||
{
|
{
|
||||||
// 如果没有创建实例,则创建对应的实例
|
|
||||||
if(!_dependencies.ContainsKey(type.FullName))
|
if(_dependencies.ContainsKey(type.FullName))
|
||||||
{
|
{
|
||||||
_dependencies[type.FullName] = Activator.CreateInstance(type);
|
// 已经存在实例,不用管
|
||||||
}
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 如果没有创建实例,则创建对应的实例
|
||||||
|
_dependencies[type.FullName] = CreateInstance(type);
|
||||||
|
}
|
||||||
|
// 移除类型的注册记录
|
||||||
|
_typeMappings.TryRemove(type.FullName,out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 注入实例的依赖项
|
// 注入实例的依赖项
|
||||||
foreach (var instance in _dependencies.Values)
|
foreach (var instance in _dependencies.Values)
|
||||||
{
|
{
|
||||||
InjectDependencies(instance); // 替换占位符
|
InjectDependencies(instance);
|
||||||
}
|
}
|
||||||
|
|
||||||
//var instance = Instantiate(item.Value);
|
//var instance = Instantiate(item.Value);
|
||||||
|
|
||||||
TryInstantiateWaitingDependencies();
|
// TryInstantiateWaitingDependencies();
|
||||||
|
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
public object Instantiate(Type controllerType, params object[] parameters)
|
public object Instantiate(Type controllerType, params object[] parameters)
|
||||||
{
|
{
|
||||||
var instance = Activator.CreateInstance(controllerType, parameters);
|
var instance = CreateInstance(controllerType, parameters);
|
||||||
if(instance != null)
|
if(instance != null)
|
||||||
{
|
{
|
||||||
InjectDependencies(instance);
|
InjectDependencies(instance);
|
||||||
@@ -175,23 +212,82 @@ namespace Serein.Library.Utils
|
|||||||
return instance;
|
return instance;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void InjectDependencies(object instance)
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注册类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="typeFull"></param>
|
||||||
|
/// <param name="type"></param>
|
||||||
|
private void RegisterType(string typeFull, Type type)
|
||||||
{
|
{
|
||||||
var properties = instance.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray()
|
if (!_typeMappings.ContainsKey(typeFull))
|
||||||
.Where(p => p.CanWrite && p.GetCustomAttribute<AutoInjectionAttribute>() != null);
|
|
||||||
|
|
||||||
foreach (var property in properties)
|
|
||||||
{
|
{
|
||||||
var propertyType = property.PropertyType;
|
_typeMappings[typeFull] = type;
|
||||||
|
|
||||||
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
|
||||||
{
|
|
||||||
property.SetValue(instance, dependencyInstance);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 创建实例时,尝试注入到由ioc容器管理、并需要此实例的对象。
|
||||||
|
/// </summary>
|
||||||
|
private object CreateInstance(Type type, params object[] parameters)
|
||||||
|
{
|
||||||
|
var instance = Activator.CreateInstance(type);
|
||||||
|
if(_unfinishedDependencies.TryGetValue(type.FullName, out var unfinishedPropertyList))
|
||||||
|
{
|
||||||
|
foreach ((object obj, PropertyInfo property) in unfinishedPropertyList)
|
||||||
|
{
|
||||||
|
property.SetValue(obj, instance); //注入依赖项
|
||||||
|
}
|
||||||
|
|
||||||
|
if(_unfinishedDependencies.TryRemove(type.FullName, out unfinishedPropertyList))
|
||||||
|
{
|
||||||
|
unfinishedPropertyList.Clear();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return instance;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 注入目标实例的依赖项
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="instance"></param>
|
||||||
|
private bool InjectDependencies(object instance)
|
||||||
|
{
|
||||||
|
var properties = instance.GetType()
|
||||||
|
.GetProperties(BindingFlags.Instance | BindingFlags.Public).ToArray()
|
||||||
|
.Where(p => p.CanWrite // 可写属性
|
||||||
|
&& p.GetCustomAttribute<AutoInjectionAttribute>() != null // 有特性标注需要注入
|
||||||
|
&& p.GetValue(instance) == null); // 属性为空
|
||||||
|
var isPass = true;
|
||||||
|
foreach (var property in properties)
|
||||||
|
{
|
||||||
|
var propertyType = property.PropertyType;
|
||||||
|
// 通过属性类型名称从ioc容器中获取对应的实例
|
||||||
|
if (_dependencies.TryGetValue(propertyType.FullName, out var dependencyInstance))
|
||||||
|
{
|
||||||
|
property.SetValue(instance, dependencyInstance); // 尝试写入到目标实例的属性中
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// 存在依赖项,但目标类型的实例暂未加载,需要等待需要实例完成注册
|
||||||
|
var unfinishedDependenciesList = _unfinishedDependencies.GetOrAdd(propertyType.FullName, _ = new List<(object, PropertyInfo)>());
|
||||||
|
var data = (instance, property);
|
||||||
|
if (!unfinishedDependenciesList.Contains(data))
|
||||||
|
{
|
||||||
|
unfinishedDependenciesList.Add(data);
|
||||||
|
}
|
||||||
|
isPass = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return isPass;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 再次尝试注入目标实例的依赖项
|
||||||
|
/// </summary>
|
||||||
private void TryInstantiateWaitingDependencies()
|
private void TryInstantiateWaitingDependencies()
|
||||||
{
|
{
|
||||||
foreach (var waitingType in _waitingForInstantiation.ToList())
|
foreach (var waitingType in _waitingForInstantiation.ToList())
|
||||||
|
|||||||
@@ -1,338 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SerinExpression
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 条件解析抽象类
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ConditionResolver
|
|
||||||
{
|
|
||||||
public abstract bool Evaluate(object obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PassConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Pass => true,
|
|
||||||
Operator.NotPass => false,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
Pass,
|
|
||||||
NotPass,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 不进行任何操作
|
|
||||||
/// </summary>
|
|
||||||
Node,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于
|
|
||||||
/// </summary>
|
|
||||||
LessThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 等于
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于或等于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于或等于
|
|
||||||
/// </summary>
|
|
||||||
LessThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 在两者之间
|
|
||||||
/// </summary>
|
|
||||||
InRange,
|
|
||||||
/// <summary>
|
|
||||||
/// 不在两者之间
|
|
||||||
/// </summary>
|
|
||||||
OutOfRange
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
public T RangeStart { get; set; }
|
|
||||||
public T RangeEnd { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is T typedObj)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(typedObj);
|
|
||||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
|
||||||
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
|
||||||
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
|
||||||
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
|
||||||
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
|
||||||
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
|
||||||
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
|
||||||
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.GreaterThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) > 0;
|
|
||||||
case Operator.LessThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) < 0;
|
|
||||||
case Operator.Equal:
|
|
||||||
return evaluatedValue.CompareTo(Value) == 0;
|
|
||||||
case Operator.GreaterThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) >= 0;
|
|
||||||
case Operator.LessThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) <= 0;
|
|
||||||
case Operator.InRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
|
||||||
case Operator.OutOfRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoolConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 是
|
|
||||||
/// </summary>
|
|
||||||
Is
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public bool Value { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj is bool boolObj)
|
|
||||||
{
|
|
||||||
return boolObj == Value;
|
|
||||||
/*switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Is:
|
|
||||||
return boolObj == Value;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 出现过
|
|
||||||
/// </summary>
|
|
||||||
Contains,
|
|
||||||
/// <summary>
|
|
||||||
/// 没有出现过
|
|
||||||
/// </summary>
|
|
||||||
DoesNotContain,
|
|
||||||
/// <summary>
|
|
||||||
/// 相等
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 不相等
|
|
||||||
/// </summary>
|
|
||||||
NotEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 起始字符串等于
|
|
||||||
/// </summary>
|
|
||||||
StartsWith,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束字符串等于
|
|
||||||
/// </summary>
|
|
||||||
EndsWith
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is string strObj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Contains => strObj.Contains(Value),
|
|
||||||
Operator.DoesNotContain => !strObj.Contains(Value),
|
|
||||||
Operator.Equal => strObj == Value,
|
|
||||||
Operator.NotEqual => strObj != Value,
|
|
||||||
Operator.StartsWith => strObj.StartsWith(Value),
|
|
||||||
Operator.EndsWith => strObj.EndsWith(Value),
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Contains:
|
|
||||||
return strObj.Contains(Value);
|
|
||||||
case Operator.DoesNotContain:
|
|
||||||
return !strObj.Contains(Value);
|
|
||||||
case Operator.Equal:
|
|
||||||
return strObj == Value;
|
|
||||||
case Operator.NotEqual:
|
|
||||||
return strObj != Value;
|
|
||||||
case Operator.StartsWith:
|
|
||||||
return strObj.StartsWith(Value);
|
|
||||||
case Operator.EndsWith:
|
|
||||||
return strObj.EndsWith(Value);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
//public string MemberPath { get; set; }
|
|
||||||
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
|
||||||
public object? TargetObj { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object? obj)
|
|
||||||
{
|
|
||||||
//object? memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (TargetObj is T typedObj)
|
|
||||||
{
|
|
||||||
return new ValueTypeConditionResolver<T>
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value,
|
|
||||||
ArithmeticExpression = ArithmeticExpression,
|
|
||||||
}.Evaluate(typedObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//private object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
//{
|
|
||||||
// string[] members = memberPath[1..].Split('.');
|
|
||||||
// foreach (var member in members)
|
|
||||||
// {
|
|
||||||
// if (obj == null) return null;
|
|
||||||
// Type type = obj.GetType();
|
|
||||||
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
// FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// obj = propertyInfo.GetValue(obj);
|
|
||||||
// else if (fieldInfo != null)
|
|
||||||
// obj = fieldInfo.GetValue(obj);
|
|
||||||
// else
|
|
||||||
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MemberStringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
|
|
||||||
public string MemberPath { get; set; }
|
|
||||||
|
|
||||||
public StringConditionResolver.Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
object memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (memberValue is string strObj)
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value
|
|
||||||
}.Evaluate(strObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SerinExpression
|
|
||||||
{
|
|
||||||
public class SerinArithmeticExpressionEvaluator
|
|
||||||
{
|
|
||||||
private static readonly DataTable table = new DataTable();
|
|
||||||
|
|
||||||
public static double Evaluate(string expression, double inputValue)
|
|
||||||
{
|
|
||||||
// 替换占位符@为输入值
|
|
||||||
expression = expression.Replace("@", inputValue.ToString());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 使用 DataTable.Compute 方法计算表达式
|
|
||||||
var result = table.Compute(expression, string.Empty);
|
|
||||||
return Convert.ToDouble(result);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid arithmetic expression.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerinExpressionEvaluator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="expression">表达式</param>
|
|
||||||
/// <param name="targetObJ">操作对象</param>
|
|
||||||
/// <param name="isChange">是否改变了对象(get语法)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="ArgumentException"></exception>
|
|
||||||
/// <exception cref="NotSupportedException"></exception>
|
|
||||||
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
|
||||||
{
|
|
||||||
var parts = expression.Split([' '], 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid expression format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var operation = parts[0].ToLower();
|
|
||||||
var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1];
|
|
||||||
|
|
||||||
var result = operation switch
|
|
||||||
{
|
|
||||||
"@num" => ComputedNumber(targetObJ, operand),
|
|
||||||
"@call" => InvokeMethod(targetObJ, operand),
|
|
||||||
"@get" => GetMember(targetObJ, operand),
|
|
||||||
"@set" => SetMember(targetObJ, operand),
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
isChange = operation switch
|
|
||||||
{
|
|
||||||
"@num" => true,
|
|
||||||
"@call" => true,
|
|
||||||
"@get" => true,
|
|
||||||
"@set" => false,
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly char[] separator = ['(', ')'];
|
|
||||||
private static readonly char[] separatorArray = [','];
|
|
||||||
|
|
||||||
private static object InvokeMethod(object target, string methodCall)
|
|
||||||
{
|
|
||||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (methodParts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid method call format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodName = methodParts[0];
|
|
||||||
var parameterList = methodParts[1];
|
|
||||||
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(p => p.Trim())
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var method = target.GetType().GetMethod(methodName);
|
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Method {methodName} not found on target.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameterValues = method.GetParameters()
|
|
||||||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
return method.Invoke(target, parameterValues);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object GetMember(object target, string memberPath)
|
|
||||||
{
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (target == null) return null;
|
|
||||||
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return target;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object SetMember(object target, string assignment)
|
|
||||||
{
|
|
||||||
var parts = assignment.Split(new[] { '=' }, 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid assignment format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var memberPath = parts[0].Trim();
|
|
||||||
var value = parts[1].Trim();
|
|
||||||
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
for (int i = 0; i < members.Length - 1; i++)
|
|
||||||
{
|
|
||||||
var member = members[i];
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastMember = members.Last();
|
|
||||||
|
|
||||||
var lastProperty = target.GetType().GetProperty(lastMember);
|
|
||||||
|
|
||||||
if (lastProperty != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
|
||||||
lastProperty.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lastField = target.GetType().GetField(lastMember);
|
|
||||||
if (lastField != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
|
||||||
lastField.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double ComputedNumber(object value, string expression)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(value);
|
|
||||||
if (!string.IsNullOrEmpty(expression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return numericValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ using Serein.Library.Api;
|
|||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
using Serein.Library.Ex;
|
using Serein.Library.Ex;
|
||||||
using Serein.NodeFlow.Tool.SerinExpression;
|
using Serein.NodeFlow.Tool.SereinExpression;
|
||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
|||||||
@@ -1,30 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow
|
|
||||||
{
|
|
||||||
public enum ConnectionType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 真分支
|
|
||||||
/// </summary>
|
|
||||||
IsSucceed,
|
|
||||||
/// <summary>
|
|
||||||
/// 假分支
|
|
||||||
/// </summary>
|
|
||||||
IsFail,
|
|
||||||
/// <summary>
|
|
||||||
/// 异常发生分支
|
|
||||||
/// </summary>
|
|
||||||
IsError,
|
|
||||||
/// <summary>
|
|
||||||
/// 上游分支(执行当前节点前会执行一次上游分支)
|
|
||||||
/// </summary>
|
|
||||||
Upstream,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,230 +0,0 @@
|
|||||||
|
|
||||||
using Serein.NodeFlow.Model;
|
|
||||||
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.NodeFlow
|
|
||||||
{
|
|
||||||
|
|
||||||
//public enum FfState
|
|
||||||
//{
|
|
||||||
// Succeed,
|
|
||||||
// Cancel,
|
|
||||||
// Error,
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public class FlipflopContext
|
|
||||||
//{
|
|
||||||
// public FlowStateType State { get; set; }
|
|
||||||
// public object? Data { get; set; }
|
|
||||||
// public FlipflopContext(FlowStateType ffState, object? data = null)
|
|
||||||
// {
|
|
||||||
// State = ffState;
|
|
||||||
// Data = data;
|
|
||||||
// }
|
|
||||||
//}
|
|
||||||
|
|
||||||
public static class FlipflopFunc
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 传入触发器方法的返回类型,尝试获取Task[Flipflop[]] 中的泛型类型
|
|
||||||
/// </summary>
|
|
||||||
//public static Type GetFlipflopInnerType(Type type)
|
|
||||||
//{
|
|
||||||
// // 检查是否为泛型类型且为 Task<>
|
|
||||||
// if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
|
||||||
// {
|
|
||||||
// // 获取 Task<> 的泛型参数类型,即 Flipflop<>
|
|
||||||
// var innerType = type.GetGenericArguments()[0];
|
|
||||||
|
|
||||||
// // 检查泛型参数是否为 Flipflop<>
|
|
||||||
// if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
|
||||||
// {
|
|
||||||
// // 获取 Flipflop<> 的泛型参数类型,即 T
|
|
||||||
// var flipflopInnerType = innerType.GetGenericArguments()[0];
|
|
||||||
|
|
||||||
// // 返回 Flipflop<> 中的具体类型
|
|
||||||
// return flipflopInnerType;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// // 如果不符合条件,返回 null
|
|
||||||
// return null;
|
|
||||||
//}
|
|
||||||
|
|
||||||
public static bool IsTaskOfFlipflop(Type type)
|
|
||||||
{
|
|
||||||
// 检查是否为泛型类型且为 Task<>
|
|
||||||
if (type.IsGenericType && type.GetGenericTypeDefinition() == typeof(Task<>))
|
|
||||||
{
|
|
||||||
// 获取 Task<> 的泛型参数类型
|
|
||||||
var innerType = type.GetGenericArguments()[0];
|
|
||||||
|
|
||||||
// 检查泛型参数是否为 Flipflop<>
|
|
||||||
if (innerType == typeof(FlipflopContext))
|
|
||||||
//if (innerType.IsGenericType && innerType.GetGenericTypeDefinition() == typeof(FlipflopContext<>))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器上下文
|
|
||||||
/// </summary>
|
|
||||||
public class FlipflopContext//<TResult>
|
|
||||||
{
|
|
||||||
public LibraryCore.NodeFlow.FlowStateType State { get; set; }
|
|
||||||
//public TResult? Data { get; set; }
|
|
||||||
public object? Data { get; set; }
|
|
||||||
public FlipflopContext(FlowStateType ffState)
|
|
||||||
{
|
|
||||||
State = ffState;
|
|
||||||
}
|
|
||||||
public FlipflopContext(FlowStateType ffState, object data)
|
|
||||||
{
|
|
||||||
State = ffState;
|
|
||||||
Data = data;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 动态流程上下文
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public class DynamicContext(ISereinIoc serviceContainer)
|
|
||||||
|
|
||||||
{
|
|
||||||
|
|
||||||
private readonly string contextGuid = "";//System.Guid.NewGuid().ToString();
|
|
||||||
|
|
||||||
public ISereinIoc ServiceContainer { get; } = serviceContainer;
|
|
||||||
private List<Type> InitServices { get; set; } = [];
|
|
||||||
|
|
||||||
// private ConcurrentDictionary<string, object?> 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<T>()
|
|
||||||
{
|
|
||||||
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<T>(string key, T value)
|
|
||||||
//{
|
|
||||||
// ContextData[key] = value;
|
|
||||||
//}
|
|
||||||
|
|
||||||
//public T? GetData<T>(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<string,Task> FlipFlopTasks { get; set; } = [];
|
|
||||||
|
|
||||||
public NodeRunTcs NodeRunCts { get; set; }
|
|
||||||
public Task CreateTimingTask(Action action, int time = 100, int count = -1)
|
|
||||||
{
|
|
||||||
NodeRunCts ??= ServiceContainer.GetOrInstantiate<NodeRunTcs>();
|
|
||||||
return Task.Factory.StartNew(async () =>
|
|
||||||
{
|
|
||||||
for (int i = 0; i < count; i++)
|
|
||||||
{
|
|
||||||
NodeRunCts.Token.ThrowIfCancellationRequested();
|
|
||||||
await Task.Delay(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");
|
|
||||||
}
|
|
||||||
@@ -90,7 +90,7 @@ namespace Serein.NodeFlow
|
|||||||
public List<MethodDetails> MethodDetailss { get; } = [];
|
public List<MethodDetails> MethodDetailss { get; } = [];
|
||||||
|
|
||||||
|
|
||||||
public Dictionary<string,NodeModelBase> Nodes { get; } = [];
|
public Dictionary<string, NodeModelBase> Nodes { get; } = [];
|
||||||
|
|
||||||
public List<NodeModelBase> Regions { get; } = [];
|
public List<NodeModelBase> Regions { get; } = [];
|
||||||
|
|
||||||
@@ -104,13 +104,15 @@ namespace Serein.NodeFlow
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// 起始节点
|
/// 起始节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public NodeModelBase StartNode {
|
public NodeModelBase StartNode
|
||||||
get
|
{
|
||||||
{
|
get
|
||||||
|
{
|
||||||
return _startNode;
|
return _startNode;
|
||||||
}
|
}
|
||||||
set {
|
set
|
||||||
if(_startNode is null)
|
{
|
||||||
|
if (_startNode is null)
|
||||||
{
|
{
|
||||||
value.IsStart = true;
|
value.IsStart = true;
|
||||||
_startNode = value;
|
_startNode = value;
|
||||||
@@ -121,7 +123,8 @@ namespace Serein.NodeFlow
|
|||||||
value.IsStart = true;
|
value.IsStart = true;
|
||||||
_startNode = value;
|
_startNode = value;
|
||||||
}
|
}
|
||||||
} }
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 异步运行
|
/// 异步运行
|
||||||
@@ -165,7 +168,7 @@ namespace Serein.NodeFlow
|
|||||||
public bool TryGetMethodDetails(string name, out MethodDetails? md)
|
public bool TryGetMethodDetails(string name, out MethodDetails? md)
|
||||||
{
|
{
|
||||||
md = MethodDetailss.FirstOrDefault(it => it.MethodName == name);
|
md = MethodDetailss.FirstOrDefault(it => it.MethodName == name);
|
||||||
if(md == null)
|
if (md == null)
|
||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
@@ -187,7 +190,7 @@ namespace Serein.NodeFlow
|
|||||||
foreach (var dll in dllPaths)
|
foreach (var dll in dllPaths)
|
||||||
{
|
{
|
||||||
var dllFilePath = System.IO.Path.GetFullPath(System.IO.Path.Combine(filePath, dll));
|
var dllFilePath = System.IO.Path.GetFullPath(System.IO.Path.Combine(filePath, dll));
|
||||||
(var assembly, var list) = LoadAssembly(dllFilePath);
|
(var assembly, var list) = LoadAssembly(dllFilePath);
|
||||||
if (assembly is not null && list.Count > 0)
|
if (assembly is not null && list.Count > 0)
|
||||||
{
|
{
|
||||||
methodDetailss.AddRange(methodDetailss); // 暂存方法描述
|
methodDetailss.AddRange(methodDetailss); // 暂存方法描述
|
||||||
@@ -195,7 +198,7 @@ namespace Serein.NodeFlow
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 方法加载完成,缓存到运行环境中。
|
// 方法加载完成,缓存到运行环境中。
|
||||||
MethodDetailss.AddRange(methodDetailss);
|
MethodDetailss.AddRange(methodDetailss);
|
||||||
methodDetailss.Clear();
|
methodDetailss.Clear();
|
||||||
|
|
||||||
|
|
||||||
@@ -216,7 +219,7 @@ namespace Serein.NodeFlow
|
|||||||
// 不存在对应的起始节点
|
// 不存在对应的起始节点
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
List<(ConnectionType, string[])> nodeGuids = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
List<(ConnectionType, string[])> nodeGuids = [(ConnectionType.IsSucceed,nodeInfo.TrueNodes),
|
||||||
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
(ConnectionType.IsFail, nodeInfo.FalseNodes),
|
||||||
@@ -256,10 +259,10 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if(fromNode is null || toNode is null)
|
if (fromNode is null || toNode is null)
|
||||||
{
|
{
|
||||||
return ;
|
return;
|
||||||
}
|
}
|
||||||
// 开始连接
|
// 开始连接
|
||||||
ConnectNode(fromNode, toNode, connectionType); // 外部调用连接方法
|
ConnectNode(fromNode, toNode, connectionType); // 外部调用连接方法
|
||||||
|
|
||||||
@@ -282,7 +285,7 @@ namespace Serein.NodeFlow
|
|||||||
NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
|
NodeControlType.ConditionRegion => typeof(CompositeConditionNode),
|
||||||
_ => null
|
_ => null
|
||||||
};
|
};
|
||||||
if(nodeType == null)
|
if (nodeType == null)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -305,7 +308,7 @@ namespace Serein.NodeFlow
|
|||||||
Nodes[nodeBase.Guid] = nodeBase;
|
Nodes[nodeBase.Guid] = nodeBase;
|
||||||
|
|
||||||
// 如果是触发器,则需要添加到专属集合中
|
// 如果是触发器,则需要添加到专属集合中
|
||||||
if (nodeControlType == NodeControlType.Flipflop && nodeBase is SingleFlipflopNode flipflopNode )
|
if (nodeControlType == NodeControlType.Flipflop && nodeBase is SingleFlipflopNode flipflopNode)
|
||||||
{
|
{
|
||||||
var guid = flipflopNode.Guid;
|
var guid = flipflopNode.Guid;
|
||||||
if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
if (!FlipflopNodes.Exists(it => it.Guid.Equals(guid)))
|
||||||
@@ -313,7 +316,7 @@ namespace Serein.NodeFlow
|
|||||||
FlipflopNodes.Add(flipflopNode);
|
FlipflopNodes.Add(flipflopNode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 通知UI更改
|
// 通知UI更改
|
||||||
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeBase));
|
OnNodeCreate?.Invoke(new NodeCreateEventArgs(nodeBase));
|
||||||
// 因为需要UI先布置了元素,才能通知UI变更特效
|
// 因为需要UI先布置了元素,才能通知UI变更特效
|
||||||
@@ -338,7 +341,7 @@ namespace Serein.NodeFlow
|
|||||||
MethodDetailss.AddRange(list);
|
MethodDetailss.AddRange(list);
|
||||||
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list));
|
OnDllLoad?.Invoke(new LoadDLLEventArgs(assembly, list));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -383,7 +386,7 @@ namespace Serein.NodeFlow
|
|||||||
NodeConnectChangeEventArgs.ChangeTypeEnum.Remote));
|
NodeConnectChangeEventArgs.ChangeTypeEnum.Remote));
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 移除节点
|
/// 移除节点
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="nodeGuid"></param>
|
/// <param name="nodeGuid"></param>
|
||||||
@@ -402,10 +405,10 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// 遍历所有父节点,从那些父节点中的子节点集合移除该节点
|
// 遍历所有父节点,从那些父节点中的子节点集合移除该节点
|
||||||
foreach(var pnc in remoteNode.PreviousNodes)
|
foreach (var pnc in remoteNode.PreviousNodes)
|
||||||
{
|
{
|
||||||
var pCType = pnc.Key; // 连接类型
|
var pCType = pnc.Key; // 连接类型
|
||||||
for (int i = 0; i < pnc.Value.Count; i++)
|
for (int i = 0; i < pnc.Value.Count; i++)
|
||||||
@@ -446,9 +449,9 @@ namespace Serein.NodeFlow
|
|||||||
/// <param name="newNodeGuid"></param>
|
/// <param name="newNodeGuid"></param>
|
||||||
public void SetStartNode(string newNodeGuid)
|
public void SetStartNode(string newNodeGuid)
|
||||||
{
|
{
|
||||||
if(Nodes.TryGetValue(newNodeGuid, out NodeModelBase? newStartNodeModel))
|
if (Nodes.TryGetValue(newNodeGuid, out NodeModelBase? newStartNodeModel))
|
||||||
{
|
{
|
||||||
if(newStartNodeModel != null)
|
if (newStartNodeModel != null)
|
||||||
{
|
{
|
||||||
SetStartNode(newStartNodeModel);
|
SetStartNode(newStartNodeModel);
|
||||||
//var oldNodeGuid = "";
|
//var oldNodeGuid = "";
|
||||||
@@ -491,7 +494,7 @@ namespace Serein.NodeFlow
|
|||||||
{
|
{
|
||||||
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
// 加载DLL,创建 MethodDetails、实例作用对象、委托方法
|
||||||
var itemMethodDetails = MethodDetailsHelperTmp.GetList(item, false);
|
var itemMethodDetails = MethodDetailsHelperTmp.GetList(item, false);
|
||||||
foreach(var md in itemMethodDetails)
|
foreach (var md in itemMethodDetails)
|
||||||
{
|
{
|
||||||
// var instanceType =
|
// var instanceType =
|
||||||
// Activator.CreateInstance(md.ActingInstanceType);
|
// Activator.CreateInstance(md.ActingInstanceType);
|
||||||
@@ -599,16 +602,7 @@ namespace Serein.NodeFlow
|
|||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class FlowFunc
|
public static class FlowFunc
|
||||||
{
|
{
|
||||||
public static Library.Entity.Library ToLibrary(this Assembly assembly)
|
public static Library.Entity.Library ToLibrary(this Assembly assembly)
|
||||||
|
|||||||
@@ -50,7 +50,7 @@ namespace Serein.NodeFlow
|
|||||||
Context = new Serein.Library.Core.NodeFlow.DynamicContext(SereinIoc, flowEnvironment);
|
Context = new Serein.Library.Core.NodeFlow.DynamicContext(SereinIoc, flowEnvironment);
|
||||||
}
|
}
|
||||||
|
|
||||||
MainCts = SereinIoc.CreateServiceInstance<NodeRunCts>();
|
MainCts = SereinIoc.GetOrCreateServiceInstance<NodeRunCts>();
|
||||||
|
|
||||||
foreach (var md in methodDetailss)
|
foreach (var md in methodDetailss)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,241 +0,0 @@
|
|||||||
using Serein.Library.Api;
|
|
||||||
using Serein.Library.Enums;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显式参数
|
|
||||||
/// </summary>
|
|
||||||
public class ExplicitData
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 索引
|
|
||||||
/// </summary>
|
|
||||||
public int Index { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为显式参数
|
|
||||||
/// </summary>
|
|
||||||
public bool IsExplicitData { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 显式类型
|
|
||||||
/// </summary>
|
|
||||||
public Type? ExplicitType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 显示类型编号>
|
|
||||||
/// </summary>
|
|
||||||
public string ExplicitTypeName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法需要的类型
|
|
||||||
/// </summary>
|
|
||||||
public Type DataType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法入参参数名称
|
|
||||||
/// </summary>
|
|
||||||
public string ParameterName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 入参值
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
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 : IMethodDetails
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 拷贝
|
|
||||||
/// </summary>
|
|
||||||
/// <returns></returns>
|
|
||||||
public MethodDetails Clone()
|
|
||||||
{
|
|
||||||
return new MethodDetails
|
|
||||||
{
|
|
||||||
ActingInstance = ActingInstance,
|
|
||||||
ActingInstanceType = ActingInstanceType,
|
|
||||||
MethodDelegate = MethodDelegate,
|
|
||||||
MethodDynamicType = MethodDynamicType,
|
|
||||||
MethodGuid = Guid.NewGuid().ToString(),
|
|
||||||
MethodTips = MethodTips,
|
|
||||||
ReturnType = ReturnType,
|
|
||||||
MethodName = MethodName,
|
|
||||||
MethodLockName = MethodLockName,
|
|
||||||
IsNetFramework = IsNetFramework,
|
|
||||||
ExplicitDatas = ExplicitDatas.Select(it => it.Clone()).ToArray(),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 作用实例
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public Type ActingInstanceType { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 作用实例
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public object ActingInstance { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法GUID
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodGuid { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法名称
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodName { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法委托
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public Delegate MethodDelegate { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 节点类型
|
|
||||||
/// </summary>
|
|
||||||
public NodeType MethodDynamicType { get; set; }
|
|
||||||
/// <summary>
|
|
||||||
/// 锁名称
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodLockName { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 方法说明
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public string MethodTips { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 参数内容
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public ExplicitData[] ExplicitDatas { get; set; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 出参类型
|
|
||||||
/// </summary>
|
|
||||||
|
|
||||||
public Type ReturnType { get; set; }
|
|
||||||
|
|
||||||
public bool IsNetFramework { 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;
|
|
||||||
//}
|
|
||||||
|
|
||||||
///// <summary>
|
|
||||||
///// 检查元组类型
|
|
||||||
///// </summary>
|
|
||||||
///// <param name="type"></param>
|
|
||||||
///// <returns></returns>
|
|
||||||
//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;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@ using Serein.Library.Api;
|
|||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
using Serein.NodeFlow.Base;
|
using Serein.NodeFlow.Base;
|
||||||
using Serein.NodeFlow.Tool.SerinExpression;
|
using Serein.NodeFlow.Tool.SereinExpression;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
{
|
{
|
||||||
@@ -42,7 +42,7 @@ namespace Serein.NodeFlow.Model
|
|||||||
}
|
}
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
var isPass = SerinConditionParser.To(result, Expression);
|
var isPass = SereinConditionParser.To(result, Expression);
|
||||||
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
|
NextOrientation = isPass ? ConnectionType.IsSucceed : ConnectionType.IsFail;
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
using Serein.Library.Entity;
|
using Serein.Library.Entity;
|
||||||
using Serein.Library.Enums;
|
using Serein.Library.Enums;
|
||||||
using Serein.NodeFlow.Base;
|
using Serein.NodeFlow.Base;
|
||||||
using Serein.NodeFlow.Tool.SerinExpression;
|
using Serein.NodeFlow.Tool.SereinExpression;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Model
|
namespace Serein.NodeFlow.Model
|
||||||
|
|||||||
@@ -1,337 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Reflection;
|
|
||||||
using System.Runtime.InteropServices;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.SerinExpression
|
|
||||||
{
|
|
||||||
|
|
||||||
public abstract class ConditionResolver
|
|
||||||
{
|
|
||||||
public abstract bool Evaluate(object obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PassConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Pass => true,
|
|
||||||
Operator.NotPass => false,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
Pass,
|
|
||||||
NotPass,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 不进行任何操作
|
|
||||||
/// </summary>
|
|
||||||
Node,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于
|
|
||||||
/// </summary>
|
|
||||||
LessThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 等于
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于或等于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于或等于
|
|
||||||
/// </summary>
|
|
||||||
LessThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 在两者之间
|
|
||||||
/// </summary>
|
|
||||||
InRange,
|
|
||||||
/// <summary>
|
|
||||||
/// 不在两者之间
|
|
||||||
/// </summary>
|
|
||||||
OutOfRange
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
public T RangeStart { get; set; }
|
|
||||||
public T RangeEnd { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is T typedObj)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(typedObj);
|
|
||||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
|
||||||
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
|
||||||
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
|
||||||
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
|
||||||
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
|
||||||
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
|
||||||
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
|
||||||
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.GreaterThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) > 0;
|
|
||||||
case Operator.LessThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) < 0;
|
|
||||||
case Operator.Equal:
|
|
||||||
return evaluatedValue.CompareTo(Value) == 0;
|
|
||||||
case Operator.GreaterThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) >= 0;
|
|
||||||
case Operator.LessThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) <= 0;
|
|
||||||
case Operator.InRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
|
||||||
case Operator.OutOfRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoolConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 是
|
|
||||||
/// </summary>
|
|
||||||
Is
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public bool Value { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj is bool boolObj)
|
|
||||||
{
|
|
||||||
return boolObj == Value;
|
|
||||||
/*switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Is:
|
|
||||||
return boolObj == Value;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 出现过
|
|
||||||
/// </summary>
|
|
||||||
Contains,
|
|
||||||
/// <summary>
|
|
||||||
/// 没有出现过
|
|
||||||
/// </summary>
|
|
||||||
DoesNotContain,
|
|
||||||
/// <summary>
|
|
||||||
/// 相等
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 不相等
|
|
||||||
/// </summary>
|
|
||||||
NotEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 起始字符串等于
|
|
||||||
/// </summary>
|
|
||||||
StartsWith,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束字符串等于
|
|
||||||
/// </summary>
|
|
||||||
EndsWith
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is string strObj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Contains => strObj.Contains(Value),
|
|
||||||
Operator.DoesNotContain => !strObj.Contains(Value),
|
|
||||||
Operator.Equal => strObj == Value,
|
|
||||||
Operator.NotEqual => strObj != Value,
|
|
||||||
Operator.StartsWith => strObj.StartsWith(Value),
|
|
||||||
Operator.EndsWith => strObj.EndsWith(Value),
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Contains:
|
|
||||||
return strObj.Contains(Value);
|
|
||||||
case Operator.DoesNotContain:
|
|
||||||
return !strObj.Contains(Value);
|
|
||||||
case Operator.Equal:
|
|
||||||
return strObj == Value;
|
|
||||||
case Operator.NotEqual:
|
|
||||||
return strObj != Value;
|
|
||||||
case Operator.StartsWith:
|
|
||||||
return strObj.StartsWith(Value);
|
|
||||||
case Operator.EndsWith:
|
|
||||||
return strObj.EndsWith(Value);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
//public string MemberPath { get; set; }
|
|
||||||
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
|
||||||
public object? TargetObj { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object? obj)
|
|
||||||
{
|
|
||||||
//object? memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (TargetObj is T typedObj)
|
|
||||||
{
|
|
||||||
return new ValueTypeConditionResolver<T>
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value,
|
|
||||||
ArithmeticExpression = ArithmeticExpression,
|
|
||||||
}.Evaluate(typedObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//private object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
//{
|
|
||||||
// string[] members = memberPath[1..].Split('.');
|
|
||||||
// foreach (var member in members)
|
|
||||||
// {
|
|
||||||
// if (obj == null) return null;
|
|
||||||
// Type type = obj.GetType();
|
|
||||||
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
// FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// obj = propertyInfo.GetValue(obj);
|
|
||||||
// else if (fieldInfo != null)
|
|
||||||
// obj = fieldInfo.GetValue(obj);
|
|
||||||
// else
|
|
||||||
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MemberStringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
|
|
||||||
public string MemberPath { get; set; }
|
|
||||||
|
|
||||||
public StringConditionResolver.Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
object memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (memberValue is string strObj)
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value
|
|
||||||
}.Evaluate(strObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,329 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Globalization;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.SerinExpression;
|
|
||||||
|
|
||||||
public class SerinConditionParser
|
|
||||||
{
|
|
||||||
public static bool To<T>(T data, string expression)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
return ConditionParse(data, expression).Evaluate(data);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConditionResolver ConditionParse(object data, string expression)
|
|
||||||
{
|
|
||||||
if (expression.StartsWith('.') /*&& expression.Contains('<') && expression.Contains('>')*/)
|
|
||||||
{
|
|
||||||
return ParseObjectExpression(data, expression);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ParseSimpleExpression(data, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
bool ContainsArithmeticOperators(string expression)
|
|
||||||
{
|
|
||||||
return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
private static object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
if (obj == null) return null;
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
int typeStartIndex = expression.IndexOf('<');
|
|
||||||
int typeEndIndex = expression.IndexOf('>');
|
|
||||||
|
|
||||||
string memberPath;
|
|
||||||
Type type;
|
|
||||||
object? targetObj;
|
|
||||||
if (typeStartIndex + typeStartIndex == -2)
|
|
||||||
{
|
|
||||||
memberPath = operatorStr;
|
|
||||||
targetObj = GetMemberValue(data, operatorStr);
|
|
||||||
|
|
||||||
type = targetObj.GetType();
|
|
||||||
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (typeStartIndex >= typeEndIndex)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("无效的表达式格式");
|
|
||||||
}
|
|
||||||
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
|
||||||
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
|
||||||
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
|
||||||
if (parts.Length == 3)
|
|
||||||
{
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operatorStr = parts[0].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(1));
|
|
||||||
}
|
|
||||||
targetObj = GetMemberValue(data, memberPath);
|
|
||||||
|
|
||||||
Type? tempType = typeStr switch
|
|
||||||
{
|
|
||||||
"int" => typeof(int),
|
|
||||||
"double" => typeof(double),
|
|
||||||
"bool" => typeof(bool),
|
|
||||||
"string" => typeof(string),
|
|
||||||
_ => Type.GetType(typeStr)
|
|
||||||
};
|
|
||||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<int>
|
|
||||||
{
|
|
||||||
TargetObj = targetObj,
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
Op = ParseValueTypeOperator<int>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<double>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
return new MemberConditionResolver<bool>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new MemberStringConditionResolver
|
|
||||||
{
|
|
||||||
MemberPath = memberPath,
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
if ("pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.Pass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ("not pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if ("!pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
|
|
||||||
if (parts.Length < 2)
|
|
||||||
throw new ArgumentException("无效的表达式格式。");
|
|
||||||
|
|
||||||
//string typeStr = parts[0];
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
Type type = data.GetType();//Type.GetType(typeStr);
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
|
||||||
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
|
||||||
{
|
|
||||||
var temp = valueStr.Split('-');
|
|
||||||
if (temp.Length < 2)
|
|
||||||
throw new ArgumentException($"范围无效:{valueStr}。");
|
|
||||||
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
|
|
||||||
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
RangeStart = rangeStart,
|
|
||||||
RangeEnd = rangeEnd,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<double>
|
|
||||||
{
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
bool value = bool.Parse(valueStr);
|
|
||||||
return new BoolConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseBoolOperator(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
|
||||||
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
|
||||||
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
|
|
||||||
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"is" => BoolConditionResolver.Operator.Is,
|
|
||||||
"==" => BoolConditionResolver.Operator.Is,
|
|
||||||
"equals" => BoolConditionResolver.Operator.Is,
|
|
||||||
//"isFalse" => BoolConditionNode.Operator.IsFalse,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"c" => StringConditionResolver.Operator.Contains,
|
|
||||||
"nc" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"sw" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"ew" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
|
|
||||||
"contains" => StringConditionResolver.Operator.Contains,
|
|
||||||
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"equals" => StringConditionResolver.Operator.Equal,
|
|
||||||
"==" => StringConditionResolver.Operator.Equal,
|
|
||||||
"notEquals" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"!=" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"startsWith" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"endsWith" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,214 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Data;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
using static Serein.NodeFlow.Model.SingleExpOpNode;
|
|
||||||
using static System.Runtime.InteropServices.JavaScript.JSType;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.SerinExpression
|
|
||||||
{
|
|
||||||
public class SerinArithmeticExpressionEvaluator
|
|
||||||
{
|
|
||||||
private static readonly DataTable table = new DataTable();
|
|
||||||
|
|
||||||
public static double Evaluate(string expression, double inputValue)
|
|
||||||
{
|
|
||||||
// 替换占位符@为输入值
|
|
||||||
expression = expression.Replace("@", inputValue.ToString());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 使用 DataTable.Compute 方法计算表达式
|
|
||||||
var result = table.Compute(expression, string.Empty);
|
|
||||||
return Convert.ToDouble(result);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid arithmetic expression.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerinExpressionEvaluator
|
|
||||||
{
|
|
||||||
public static object Evaluate(string expression, object targetObJ, out bool IsChange)
|
|
||||||
{
|
|
||||||
var parts = expression.Split([' '], 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid expression format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var operation = parts[0].ToLower();
|
|
||||||
var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1];
|
|
||||||
|
|
||||||
var result = operation switch
|
|
||||||
{
|
|
||||||
"@num" => ComputedNumber(targetObJ, operand),
|
|
||||||
"@call" => InvokeMethod(targetObJ, operand),
|
|
||||||
"@get" => GetMember(targetObJ, operand),
|
|
||||||
"@set" => SetMember(targetObJ, operand),
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
IsChange = operation switch
|
|
||||||
{
|
|
||||||
"@num" => true,
|
|
||||||
"@call" => true,
|
|
||||||
"@get" => true,
|
|
||||||
"@set" => false,
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly char[] separator = ['(', ')'];
|
|
||||||
private static readonly char[] separatorArray = [','];
|
|
||||||
|
|
||||||
private static object InvokeMethod(object target, string methodCall)
|
|
||||||
{
|
|
||||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (methodParts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid method call format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodName = methodParts[0];
|
|
||||||
var parameterList = methodParts[1];
|
|
||||||
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(p => p.Trim())
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var method = target.GetType().GetMethod(methodName);
|
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Method {methodName} not found on target.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameterValues = method.GetParameters()
|
|
||||||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
return method.Invoke(target, parameterValues);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object GetMember(object target, string memberPath)
|
|
||||||
{
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (target == null) return null;
|
|
||||||
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return target;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object SetMember(object target, string assignment)
|
|
||||||
{
|
|
||||||
var parts = assignment.Split(new[] { '=' }, 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid assignment format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var memberPath = parts[0].Trim();
|
|
||||||
var value = parts[1].Trim();
|
|
||||||
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
for (int i = 0; i < members.Length - 1; i++)
|
|
||||||
{
|
|
||||||
var member = members[i];
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastMember = members.Last();
|
|
||||||
|
|
||||||
var lastProperty = target.GetType().GetProperty(lastMember);
|
|
||||||
|
|
||||||
if (lastProperty != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
|
||||||
lastProperty.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lastField = target.GetType().GetField(lastMember);
|
|
||||||
if (lastField != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
|
||||||
lastField.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double ComputedNumber(object value, string expression)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(value);
|
|
||||||
if (!string.IsNullOrEmpty(expression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return numericValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,73 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool
|
|
||||||
{
|
|
||||||
|
|
||||||
public enum DynamicNodeType
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 初始化
|
|
||||||
/// </summary>
|
|
||||||
Init,
|
|
||||||
/// <summary>
|
|
||||||
/// 开始载入
|
|
||||||
/// </summary>
|
|
||||||
Loading,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束
|
|
||||||
/// </summary>
|
|
||||||
Exit,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发器
|
|
||||||
/// </summary>
|
|
||||||
Flipflop,
|
|
||||||
/// <summary>
|
|
||||||
/// 条件节点
|
|
||||||
/// </summary>
|
|
||||||
Condition,
|
|
||||||
/// <summary>
|
|
||||||
/// 动作节点
|
|
||||||
/// </summary>
|
|
||||||
Action,
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 用来判断一个类是否需要注册并构建实例(单例模式场景使用)
|
|
||||||
/// </summary>
|
|
||||||
[AttributeUsage(AttributeTargets.Class)]
|
|
||||||
public class DynamicFlowAttribute(bool scan = true) : Attribute
|
|
||||||
{
|
|
||||||
public bool Scan { get; set; } = scan;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 标记一个方法是什么类型,加载dll后用来拖拽到画布中
|
|
||||||
/// </summary>
|
|
||||||
[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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 是否为显式参数
|
|
||||||
/// </summary>
|
|
||||||
//[AttributeUsage(AttributeTargets.Parameter)]
|
|
||||||
//public class ExplicitAttribute : Attribute // where TEnum : Enum
|
|
||||||
//{
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,202 +0,0 @@
|
|||||||
using System;
|
|
||||||
using System.Collections.Concurrent;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Drawing.Printing;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool
|
|
||||||
{
|
|
||||||
|
|
||||||
#region 锁、tsk工具 (已注释)
|
|
||||||
/*public class LockManager
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, LockQueue> _locks = new ConcurrentDictionary<string, LockQueue>();
|
|
||||||
|
|
||||||
public void CreateLock(string name)
|
|
||||||
{
|
|
||||||
_locks.TryAdd(name, new LockQueue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task AcquireLockAsync(string name, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!_locks.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockQueue = _locks[name];
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
lockQueue.Queue.Enqueue(tcs);
|
|
||||||
if (lockQueue.Queue.Count == 1)
|
|
||||||
{
|
|
||||||
tcs.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// 处理取消操作
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
cancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
if (lockQueue.Queue.Contains(tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ReleaseLock(string name)
|
|
||||||
{
|
|
||||||
if (!_locks.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Lock with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var lockQueue = _locks[name];
|
|
||||||
|
|
||||||
lock (lockQueue.Queue)
|
|
||||||
{
|
|
||||||
if (lockQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
lockQueue.Queue.Dequeue();
|
|
||||||
|
|
||||||
if (lockQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
var next = lockQueue.Queue.Peek();
|
|
||||||
next.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class LockQueue
|
|
||||||
{
|
|
||||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public interface ITaskResult
|
|
||||||
{
|
|
||||||
object Result { get; }
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TaskResult<T> : ITaskResult
|
|
||||||
{
|
|
||||||
public TaskResult(T result)
|
|
||||||
{
|
|
||||||
Result = result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public T Result { get; }
|
|
||||||
|
|
||||||
object ITaskResult.Result => Result;
|
|
||||||
}
|
|
||||||
|
|
||||||
public class DynamicTasks
|
|
||||||
{
|
|
||||||
private static readonly ConcurrentDictionary<string, Task<ITaskResult>> TaskGuidPairs = new();
|
|
||||||
public static Task<ITaskResult> GetTask(string Guid)
|
|
||||||
{
|
|
||||||
TaskGuidPairs.TryGetValue(Guid, out Task<ITaskResult> task);
|
|
||||||
return task;
|
|
||||||
}
|
|
||||||
|
|
||||||
public static bool AddTask<T>(string Guid, T result)
|
|
||||||
{
|
|
||||||
var task = Task.FromResult<ITaskResult>(new TaskResult<T>(result));
|
|
||||||
|
|
||||||
return TaskGuidPairs.TryAdd(Guid, task);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class TaskNodeManager
|
|
||||||
{
|
|
||||||
private readonly ConcurrentDictionary<string, TaskQueue> _taskQueues = new ConcurrentDictionary<string, TaskQueue>();
|
|
||||||
|
|
||||||
public void CreateTaskNode(string name)
|
|
||||||
{
|
|
||||||
_taskQueues.TryAdd(name, new TaskQueue());
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task WaitForTaskNodeAsync(string name, CancellationToken cancellationToken = default)
|
|
||||||
{
|
|
||||||
if (!_taskQueues.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskQueue = _taskQueues[name];
|
|
||||||
var tcs = new TaskCompletionSource<bool>(TaskCreationOptions.RunContinuationsAsynchronously);
|
|
||||||
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
taskQueue.Queue.Enqueue(tcs);
|
|
||||||
if (taskQueue.Queue.Count == 1)
|
|
||||||
{
|
|
||||||
tcs.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await tcs.Task.ConfigureAwait(false);
|
|
||||||
|
|
||||||
// 处理取消操作
|
|
||||||
if (cancellationToken.CanBeCanceled)
|
|
||||||
{
|
|
||||||
cancellationToken.Register(() =>
|
|
||||||
{
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
if (taskQueue.Queue.Contains(tcs))
|
|
||||||
{
|
|
||||||
tcs.TrySetCanceled();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CompleteTaskNode(string name)
|
|
||||||
{
|
|
||||||
if (!_taskQueues.ContainsKey(name))
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Task node with name '{name}' does not exist.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var taskQueue = _taskQueues[name];
|
|
||||||
|
|
||||||
lock (taskQueue.Queue)
|
|
||||||
{
|
|
||||||
if (taskQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
taskQueue.Queue.Dequeue();
|
|
||||||
|
|
||||||
if (taskQueue.Queue.Count > 0)
|
|
||||||
{
|
|
||||||
var next = taskQueue.Queue.Peek();
|
|
||||||
next.SetResult(true);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private class TaskQueue
|
|
||||||
{
|
|
||||||
public Queue<TaskCompletionSource<bool>> Queue { get; } = new Queue<TaskCompletionSource<bool>>();
|
|
||||||
}
|
|
||||||
}*/
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,460 +0,0 @@
|
|||||||
using Newtonsoft.Json;
|
|
||||||
using Serein.Library.Api;
|
|
||||||
using Serein.Library.Entity;
|
|
||||||
using Serein.Library.Enums;
|
|
||||||
using Serein.NodeFlow.Tool.SerinExpression;
|
|
||||||
using System;
|
|
||||||
using System.Collections.Generic;
|
|
||||||
using System.Linq;
|
|
||||||
using System.Text;
|
|
||||||
using System.Threading.Tasks;
|
|
||||||
|
|
||||||
namespace Serein.Library.Base
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 节点基类(数据):条件控件,动作控件,条件区域,动作区域
|
|
||||||
/// </summary>
|
|
||||||
// public abstract partial class NodeModelBase : IDynamicFlowNode
|
|
||||||
public static class NodeFunc
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 执行节点对应的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context">流程上下文</param>
|
|
||||||
/// <returns>节点传回数据对象</returns>
|
|
||||||
public static object? Execute(this NodeModelBase nodeModelBase, IDynamicContext context)
|
|
||||||
{
|
|
||||||
MethodDetails md = nodeModelBase.MethodDetails;
|
|
||||||
object? result = null;
|
|
||||||
var del = md.MethodDelegate;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (md.ExplicitDatas.Length == 0)
|
|
||||||
{
|
|
||||||
if (md.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
object?[]? parameters = nodeModelBase.GetParameters(context, md);
|
|
||||||
if (md.ReturnType == typeof(void))
|
|
||||||
{
|
|
||||||
((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
nodeModelBase.FlowState = FlowStateType.Error;
|
|
||||||
nodeModelBase.RuningException = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 执行等待触发器的方法
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns>节点传回数据对象</returns>
|
|
||||||
/// <exception cref="RuningException"></exception>
|
|
||||||
public static async Task<object?> ExecuteAsync(this NodeModelBase nodeModel, IDynamicContext context)
|
|
||||||
{
|
|
||||||
MethodDetails md = nodeModel.MethodDetails;
|
|
||||||
object? result = null;
|
|
||||||
|
|
||||||
IFlipflopContext flipflopContext = null;
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 调用委托并获取结果
|
|
||||||
if (md.ExplicitDatas.Length == 0)
|
|
||||||
{
|
|
||||||
flipflopContext = await ((Func<object, Task<IFlipflopContext>>)md.MethodDelegate).Invoke(md.ActingInstance);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
object?[]? parameters = nodeModel.GetParameters(context, md);
|
|
||||||
flipflopContext = await ((Func<object, object[], Task<IFlipflopContext>>)md.MethodDelegate).Invoke(md.ActingInstance, parameters);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (flipflopContext != null)
|
|
||||||
{
|
|
||||||
nodeModel.FlowState = flipflopContext.State;
|
|
||||||
if (flipflopContext.State == FlowStateType.Succeed)
|
|
||||||
{
|
|
||||||
result = flipflopContext.Data;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
result = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
nodeModel.FlowState = FlowStateType.Error;
|
|
||||||
nodeModel.RuningException = ex;
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 开始执行
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="context"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public static async Task StartExecution(this NodeModelBase nodeModel, IDynamicContext context)
|
|
||||||
{
|
|
||||||
var cts = context.SereinIoc.GetOrInstantiate<CancellationTokenSource>();
|
|
||||||
|
|
||||||
Stack<NodeModelBase> stack = [];
|
|
||||||
stack.Push(nodeModel);
|
|
||||||
var md = nodeModel.MethodDetails;
|
|
||||||
while (stack.Count > 0 && !cts.IsCancellationRequested) // 循环中直到栈为空才会退出循环
|
|
||||||
{
|
|
||||||
// 从栈中弹出一个节点作为当前节点进行处理
|
|
||||||
var currentNode = stack.Pop();
|
|
||||||
|
|
||||||
// 设置方法执行的对象
|
|
||||||
if (currentNode.MethodDetails != null)
|
|
||||||
{
|
|
||||||
currentNode.MethodDetails.ActingInstance ??= context.SereinIoc.GetOrInstantiate(md.ActingInstanceType);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取上游分支,首先执行一次
|
|
||||||
var upstreamNodes = currentNode.UpstreamBranch;
|
|
||||||
for (int i = upstreamNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
upstreamNodes[i].PreviousNode = currentNode;
|
|
||||||
await upstreamNodes[i].StartExecution(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentNode.MethodDetails != null && currentNode.MethodDetails.MethodDynamicType == NodeType.Flipflop)
|
|
||||||
{
|
|
||||||
// 触发器节点
|
|
||||||
currentNode.FlowData = await currentNode.ExecuteAsync(context);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// 动作节点
|
|
||||||
currentNode.FlowData = currentNode.Execute(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
var nextNodes = currentNode.FlowState switch
|
|
||||||
{
|
|
||||||
FlowStateType.Succeed => currentNode.SucceedBranch,
|
|
||||||
FlowStateType.Fail => currentNode.FailBranch,
|
|
||||||
FlowStateType.Error => currentNode.ErrorBranch,
|
|
||||||
_ => throw new Exception("非预期的枚举值")
|
|
||||||
};
|
|
||||||
|
|
||||||
// 将下一个节点集合中的所有节点逆序推入栈中
|
|
||||||
for (int i = nextNodes.Count - 1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
nextNodes[i].PreviousNode = currentNode;
|
|
||||||
stack.Push(nextNodes[i]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对应的参数数组
|
|
||||||
/// </summary>
|
|
||||||
public static object[]? GetParameters(this NodeModelBase nodeModel, IDynamicContext context, MethodDetails md)
|
|
||||||
{
|
|
||||||
// 用正确的大小初始化参数数组
|
|
||||||
var types = md.ExplicitDatas.Select(it => it.DataType).ToArray();
|
|
||||||
if (types.Length == 0)
|
|
||||||
{
|
|
||||||
return [md.ActingInstance];
|
|
||||||
}
|
|
||||||
|
|
||||||
object[]? parameters = new object[types.Length];
|
|
||||||
|
|
||||||
for (int i = 0; i < types.Length; i++)
|
|
||||||
{
|
|
||||||
|
|
||||||
var mdEd = md.ExplicitDatas[i];
|
|
||||||
Type type = mdEd.DataType;
|
|
||||||
|
|
||||||
var f1 = nodeModel.PreviousNode?.FlowData?.GetType();
|
|
||||||
var f2 = mdEd.DataType;
|
|
||||||
if (type == typeof(IDynamicContext))
|
|
||||||
{
|
|
||||||
parameters[i] = context;
|
|
||||||
}
|
|
||||||
else if (type == typeof(MethodDetails))
|
|
||||||
{
|
|
||||||
parameters[i] = md;
|
|
||||||
}
|
|
||||||
else if (type == typeof(NodeModelBase))
|
|
||||||
{
|
|
||||||
parameters[i] = nodeModel;
|
|
||||||
}
|
|
||||||
else if (mdEd.IsExplicitData) // 显式参数
|
|
||||||
{
|
|
||||||
// 判断是否使用表达式解析
|
|
||||||
if (mdEd.DataValue[0] == '@')
|
|
||||||
{
|
|
||||||
var expResult = SerinExpressionEvaluator.Evaluate(mdEd.DataValue, nodeModel.PreviousNode?.FlowData, out bool isChange);
|
|
||||||
|
|
||||||
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(string))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(string));
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(bool))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(bool));
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(int))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(int));
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(double))
|
|
||||||
{
|
|
||||||
parameters[i] = Convert.ChangeType(expResult, typeof(double));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = expResult;
|
|
||||||
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, mdEd.DataValue);
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(string))
|
|
||||||
{
|
|
||||||
parameters[i] = mdEd.DataValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(bool))
|
|
||||||
{
|
|
||||||
parameters[i] = bool.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(int))
|
|
||||||
{
|
|
||||||
parameters[i] = int.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else if (mdEd.ExplicitType == typeof(double))
|
|
||||||
{
|
|
||||||
parameters[i] = double.Parse(mdEd.DataValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
parameters[i] = "";
|
|
||||||
|
|
||||||
//parameters[i] = ConvertValue(mdEd.DataValue, mdEd.ExplicitType);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (f1 != null && f2 != null)
|
|
||||||
{
|
|
||||||
if (f2.IsAssignableFrom(f1) || f2.FullName.Equals(f1.FullName))
|
|
||||||
{
|
|
||||||
parameters[i] = nodeModel.PreviousNode?.FlowData;
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
|
|
||||||
|
|
||||||
var tmpParameter = nodeModel.PreviousNode?.FlowData?.ToString();
|
|
||||||
if (mdEd.DataType.IsEnum)
|
|
||||||
{
|
|
||||||
|
|
||||||
var enumValue = Enum.Parse(mdEd.DataType, tmpParameter);
|
|
||||||
|
|
||||||
parameters[i] = enumValue;
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(string))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = tmpParameter;
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(bool))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = bool.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(int))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = int.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (mdEd.DataType == typeof(double))
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = double.Parse(tmpParameter);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (tmpParameter != null && mdEd.DataType != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
parameters[i] = ConvertValue(tmpParameter, mdEd.DataType);
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
return parameters;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// json文本反序列化为对象
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="value"></param>
|
|
||||||
/// <param name="targetType"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static dynamic? ConvertValue(string value, Type targetType)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (!string.IsNullOrEmpty(value))
|
|
||||||
{
|
|
||||||
return JsonConvert.DeserializeObject(value, targetType);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (JsonReaderException ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
catch (JsonSerializationException ex)
|
|
||||||
{
|
|
||||||
// 如果无法转为对应的JSON对象
|
|
||||||
int startIndex = ex.Message.IndexOf("to type '") + "to type '".Length; // 查找类型信息开始的索引
|
|
||||||
int endIndex = ex.Message.IndexOf('\''); // 查找类型信息结束的索引
|
|
||||||
var typeInfo = ex.Message[startIndex..endIndex]; // 提取出错类型信息,该怎么传出去?
|
|
||||||
Console.WriteLine("无法转为对应的JSON对象:" + typeInfo);
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
catch // (Exception ex)
|
|
||||||
{
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#region 完整的ExecuteAsync调用方法(不要删除)
|
|
||||||
//public virtual async Task<object?> ExecuteAsync(DynamicContext context)
|
|
||||||
//{
|
|
||||||
// MethodDetails md = MethodDetails;
|
|
||||||
// object? result = null;
|
|
||||||
// if (DelegateCache.GlobalDicDelegates.TryGetValue(md.MethodName, out Delegate del))
|
|
||||||
// {
|
|
||||||
// if (md.ExplicitDatas.Length == 0)
|
|
||||||
// {
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// else if (md.ReturnType == typeof(Task<FlipflopContext>))
|
|
||||||
// {
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// FlipflopContext flipflopContext = await ((Func<object, Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance);
|
|
||||||
|
|
||||||
// if (flipflopContext != null)
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Cancel)
|
|
||||||
// {
|
|
||||||
// throw new Exception("this async task is cancel.");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Succeed)
|
|
||||||
// {
|
|
||||||
// CurrentState = true;
|
|
||||||
// result = flipflopContext.Data;
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// CurrentState = false;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object>)del).Invoke(md.ActingInstance);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// object?[]? parameters = GetParameters(context, MethodDetails);
|
|
||||||
// if (md.ReturnType == typeof(void))
|
|
||||||
// {
|
|
||||||
// ((Action<object, object[]>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// else if (md.ReturnType == typeof(Task<FlipflopContext>))
|
|
||||||
// {
|
|
||||||
// // 调用委托并获取结果
|
|
||||||
// FlipflopContext flipflopContext = await ((Func<object, object[], Task<FlipflopContext>>)del).Invoke(MethodDetails.ActingInstance, parameters);
|
|
||||||
|
|
||||||
// if (flipflopContext != null)
|
|
||||||
// {
|
|
||||||
// if (flipflopContext.State == FfState.Cancel)
|
|
||||||
// {
|
|
||||||
// throw new Exception("取消此异步");
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// CurrentState = flipflopContext.State == FfState.Succeed;
|
|
||||||
// result = flipflopContext.Data;
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// else
|
|
||||||
// {
|
|
||||||
// result = ((Func<object, object[], object>)del).Invoke(md.ActingInstance, parameters);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
// context.SetFlowData(result);
|
|
||||||
// }
|
|
||||||
// return result;
|
|
||||||
//}
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||||
|
{
|
||||||
|
public class BoolConditionResolver : SereinConditionResolver
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 是
|
||||||
|
/// </summary>
|
||||||
|
Is
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public bool Value { get; set; }
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (obj is bool boolObj)
|
||||||
|
{
|
||||||
|
return boolObj == Value;
|
||||||
|
/*switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Is:
|
||||||
|
return boolObj == Value;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,53 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||||
|
{
|
||||||
|
public class MemberConditionResolver<T> : SereinConditionResolver where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
//public string MemberPath { get; set; }
|
||||||
|
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
||||||
|
public object? TargetObj { get; set; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
|
||||||
|
public string ArithmeticExpression { get; set; }
|
||||||
|
|
||||||
|
public override bool Evaluate(object? obj)
|
||||||
|
{
|
||||||
|
//object? memberValue = GetMemberValue(obj, MemberPath);
|
||||||
|
if (TargetObj is T typedObj)
|
||||||
|
{
|
||||||
|
return new ValueTypeConditionResolver<T>
|
||||||
|
{
|
||||||
|
Op = Op,
|
||||||
|
Value = Value,
|
||||||
|
ArithmeticExpression = ArithmeticExpression,
|
||||||
|
}.Evaluate(typedObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
//private object? GetMemberValue(object? obj, string memberPath)
|
||||||
|
//{
|
||||||
|
// string[] members = memberPath[1..].Split('.');
|
||||||
|
// foreach (var member in members)
|
||||||
|
// {
|
||||||
|
// if (obj == null) return null;
|
||||||
|
// Type type = obj.GetType();
|
||||||
|
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
// FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
// if (propertyInfo != null)
|
||||||
|
// obj = propertyInfo.GetValue(obj);
|
||||||
|
// else if (fieldInfo != null)
|
||||||
|
// obj = fieldInfo.GetValue(obj);
|
||||||
|
// else
|
||||||
|
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
// }
|
||||||
|
// return obj;
|
||||||
|
//}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,76 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Reflection;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||||
|
{
|
||||||
|
public class MemberStringConditionResolver : SereinConditionResolver
|
||||||
|
{
|
||||||
|
public string MemberPath { get; set; }
|
||||||
|
|
||||||
|
public StringConditionResolver.Operator Op { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
object memberValue = GetMemberValue(obj, MemberPath);
|
||||||
|
if (memberValue is string strObj)
|
||||||
|
{
|
||||||
|
return new StringConditionResolver
|
||||||
|
{
|
||||||
|
Op = Op,
|
||||||
|
Value = Value
|
||||||
|
}.Evaluate(strObj);
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
private object GetMemberValue(object? obj, string memberPath)
|
||||||
|
{
|
||||||
|
string[] members = memberPath[1..].Split('.');
|
||||||
|
foreach (var member in members)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (obj == null) return null;
|
||||||
|
|
||||||
|
Type type = obj.GetType();
|
||||||
|
PropertyInfo? propertyInfo = type.GetProperty(member);
|
||||||
|
FieldInfo? fieldInfo = type.GetField(member);
|
||||||
|
if (propertyInfo != null)
|
||||||
|
obj = propertyInfo.GetValue(obj);
|
||||||
|
else if (fieldInfo != null)
|
||||||
|
obj = fieldInfo.GetValue(obj);
|
||||||
|
else
|
||||||
|
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static string GetArithmeticExpression(string part)
|
||||||
|
{
|
||||||
|
int startIndex = part.IndexOf('[');
|
||||||
|
int endIndex = part.IndexOf(']');
|
||||||
|
if (startIndex >= 0 && endIndex > startIndex)
|
||||||
|
{
|
||||||
|
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||||
|
{
|
||||||
|
public class PassConditionResolver : SereinConditionResolver
|
||||||
|
{
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
/*return Op switch
|
||||||
|
{
|
||||||
|
Operator.Pass => true,
|
||||||
|
Operator.NotPass => false,
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型")
|
||||||
|
};*/
|
||||||
|
switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Pass:
|
||||||
|
return true;
|
||||||
|
case Operator.NotPass:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException("不支持的条件类型");
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
Pass,
|
||||||
|
NotPass,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||||
|
{
|
||||||
|
public class StringConditionResolver : SereinConditionResolver
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 出现过
|
||||||
|
/// </summary>
|
||||||
|
Contains,
|
||||||
|
/// <summary>
|
||||||
|
/// 没有出现过
|
||||||
|
/// </summary>
|
||||||
|
DoesNotContain,
|
||||||
|
/// <summary>
|
||||||
|
/// 相等
|
||||||
|
/// </summary>
|
||||||
|
Equal,
|
||||||
|
/// <summary>
|
||||||
|
/// 不相等
|
||||||
|
/// </summary>
|
||||||
|
NotEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 起始字符串等于
|
||||||
|
/// </summary>
|
||||||
|
StartsWith,
|
||||||
|
/// <summary>
|
||||||
|
/// 结束字符串等于
|
||||||
|
/// </summary>
|
||||||
|
EndsWith
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
|
||||||
|
public string Value { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
if (obj is string strObj)
|
||||||
|
{
|
||||||
|
/*return Op switch
|
||||||
|
{
|
||||||
|
Operator.Contains => strObj.Contains(Value),
|
||||||
|
Operator.DoesNotContain => !strObj.Contains(Value),
|
||||||
|
Operator.Equal => strObj == Value,
|
||||||
|
Operator.NotEqual => strObj != Value,
|
||||||
|
Operator.StartsWith => strObj.StartsWith(Value),
|
||||||
|
Operator.EndsWith => strObj.EndsWith(Value),
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型"),
|
||||||
|
};*/
|
||||||
|
|
||||||
|
switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.Contains:
|
||||||
|
return strObj.Contains(Value);
|
||||||
|
case Operator.DoesNotContain:
|
||||||
|
return !strObj.Contains(Value);
|
||||||
|
case Operator.Equal:
|
||||||
|
return strObj == Value;
|
||||||
|
case Operator.NotEqual:
|
||||||
|
return strObj != Value;
|
||||||
|
case Operator.StartsWith:
|
||||||
|
return strObj.StartsWith(Value);
|
||||||
|
case Operator.EndsWith:
|
||||||
|
return strObj.EndsWith(Value);
|
||||||
|
default:
|
||||||
|
throw new NotSupportedException("不支持的条件类型");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,100 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression.Resolver
|
||||||
|
{
|
||||||
|
public class ValueTypeConditionResolver<T> : SereinConditionResolver where T : struct, IComparable<T>
|
||||||
|
{
|
||||||
|
public enum Operator
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 不进行任何操作
|
||||||
|
/// </summary>
|
||||||
|
Node,
|
||||||
|
/// <summary>
|
||||||
|
/// 大于
|
||||||
|
/// </summary>
|
||||||
|
GreaterThan,
|
||||||
|
/// <summary>
|
||||||
|
/// 小于
|
||||||
|
/// </summary>
|
||||||
|
LessThan,
|
||||||
|
/// <summary>
|
||||||
|
/// 等于
|
||||||
|
/// </summary>
|
||||||
|
Equal,
|
||||||
|
/// <summary>
|
||||||
|
/// 大于或等于
|
||||||
|
/// </summary>
|
||||||
|
GreaterThanOrEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 小于或等于
|
||||||
|
/// </summary>
|
||||||
|
LessThanOrEqual,
|
||||||
|
/// <summary>
|
||||||
|
/// 在两者之间
|
||||||
|
/// </summary>
|
||||||
|
InRange,
|
||||||
|
/// <summary>
|
||||||
|
/// 不在两者之间
|
||||||
|
/// </summary>
|
||||||
|
OutOfRange
|
||||||
|
}
|
||||||
|
|
||||||
|
public Operator Op { get; set; }
|
||||||
|
public T Value { get; set; }
|
||||||
|
public T RangeStart { get; set; }
|
||||||
|
public T RangeEnd { get; set; }
|
||||||
|
|
||||||
|
public string ArithmeticExpression { get; set; }
|
||||||
|
|
||||||
|
|
||||||
|
public override bool Evaluate(object obj)
|
||||||
|
{
|
||||||
|
if (obj is T typedObj)
|
||||||
|
{
|
||||||
|
double numericValue = Convert.ToDouble(typedObj);
|
||||||
|
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
||||||
|
{
|
||||||
|
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
||||||
|
}
|
||||||
|
|
||||||
|
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
||||||
|
|
||||||
|
/*return Op switch
|
||||||
|
{
|
||||||
|
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
||||||
|
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
||||||
|
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
||||||
|
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
||||||
|
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
||||||
|
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
||||||
|
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
||||||
|
_ => throw new NotSupportedException("不支持的条件类型")
|
||||||
|
};*/
|
||||||
|
switch (Op)
|
||||||
|
{
|
||||||
|
case Operator.GreaterThan:
|
||||||
|
return evaluatedValue.CompareTo(Value) > 0;
|
||||||
|
case Operator.LessThan:
|
||||||
|
return evaluatedValue.CompareTo(Value) < 0;
|
||||||
|
case Operator.Equal:
|
||||||
|
return evaluatedValue.CompareTo(Value) == 0;
|
||||||
|
case Operator.GreaterThanOrEqual:
|
||||||
|
return evaluatedValue.CompareTo(Value) >= 0;
|
||||||
|
case Operator.LessThanOrEqual:
|
||||||
|
return evaluatedValue.CompareTo(Value) <= 0;
|
||||||
|
case Operator.InRange:
|
||||||
|
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
||||||
|
case Operator.OutOfRange:
|
||||||
|
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,11 +1,11 @@
|
|||||||
using System;
|
using Serein.NodeFlow.Tool.SereinExpression.Resolver;
|
||||||
using System.Globalization;
|
using System.Globalization;
|
||||||
using System.Reflection;
|
using System.Reflection;
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SerinExpression
|
namespace Serein.NodeFlow.Tool.SereinExpression
|
||||||
{
|
{
|
||||||
|
|
||||||
public class SerinConditionParser
|
public class SereinConditionParser
|
||||||
{
|
{
|
||||||
public static bool To<T>(T data, string expression)
|
public static bool To<T>(T data, string expression)
|
||||||
{
|
{
|
||||||
@@ -22,7 +22,7 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ConditionResolver ConditionParse(object data, string expression)
|
public static SereinConditionResolver ConditionParse(object data, string expression)
|
||||||
{
|
{
|
||||||
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
||||||
{
|
{
|
||||||
@@ -79,51 +79,57 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
}
|
}
|
||||||
return obj;
|
return obj;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// 解析对象表达式
|
/// 解析对象表达式
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
private static SereinConditionResolver ParseObjectExpression(object data, string expression)
|
||||||
{
|
{
|
||||||
var parts = expression.Split(' ');
|
var parts = expression.Split(' ');
|
||||||
string operatorStr = parts[0];
|
string operatorStr = parts[0]; // 获取操作类型
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
string valueStr; //= string.Join(' ', parts, 1, parts.Length - 1);
|
||||||
|
|
||||||
int typeStartIndex = expression.IndexOf('<');
|
|
||||||
int typeEndIndex = expression.IndexOf('>');
|
|
||||||
|
|
||||||
string memberPath;
|
string memberPath;
|
||||||
Type type;
|
Type type;
|
||||||
object? targetObj;
|
object? targetObj;
|
||||||
|
|
||||||
|
// 尝试获取指定类型
|
||||||
|
int typeStartIndex = expression.IndexOf('<');
|
||||||
|
int typeEndIndex = expression.IndexOf('>');
|
||||||
if (typeStartIndex + typeStartIndex == -2)
|
if (typeStartIndex + typeStartIndex == -2)
|
||||||
{
|
{
|
||||||
|
// 如果不需要转为指定类型
|
||||||
memberPath = operatorStr;
|
memberPath = operatorStr;
|
||||||
targetObj = GetMemberValue(data, operatorStr);
|
targetObj = GetMemberValue(data, operatorStr);
|
||||||
|
|
||||||
type = targetObj.GetType();
|
type = targetObj.GetType();
|
||||||
|
operatorStr = parts[1].ToLower(); //
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
valueStr = string.Join(' ', parts.Skip(2));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
// 类型语法不正确
|
||||||
if (typeStartIndex >= typeEndIndex)
|
if (typeStartIndex >= typeEndIndex)
|
||||||
{
|
{
|
||||||
throw new ArgumentException("无效的表达式格式");
|
throw new ArgumentException("无效的表达式格式");
|
||||||
}
|
}
|
||||||
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
||||||
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1)
|
||||||
|
.Trim().ToLower(); // 手动置顶的类型
|
||||||
|
|
||||||
|
// 对象取值表达式
|
||||||
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
||||||
if (parts.Length == 3)
|
if (parts.Length == 3)
|
||||||
{
|
{
|
||||||
operatorStr = parts[1].ToLower();
|
operatorStr = parts[1].ToLower(); // 操作类型
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
valueStr = string.Join(' ', parts.Skip(2)); // 表达式值
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
operatorStr = parts[0].ToLower();
|
operatorStr = parts[0].ToLower(); // 操作类型
|
||||||
valueStr = string.Join(' ', parts.Skip(1));
|
valueStr = string.Join(' ', parts.Skip(1)); // 表达式值
|
||||||
}
|
}
|
||||||
targetObj = GetMemberValue(data, memberPath);
|
|
||||||
|
targetObj = GetMemberValue(data, memberPath);// 获取对象成员,作为表达式的目标对象
|
||||||
|
|
||||||
Type? tempType = typeStr switch
|
Type? tempType = typeStr switch
|
||||||
{
|
{
|
||||||
@@ -136,8 +142,7 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#region 解析类型 int
|
||||||
|
|
||||||
if (type == typeof(int))
|
if (type == typeof(int))
|
||||||
{
|
{
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
@@ -150,6 +155,8 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
#region 解析类型 double
|
||||||
else if (type == typeof(double))
|
else if (type == typeof(double))
|
||||||
{
|
{
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
||||||
@@ -163,6 +170,8 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
#region 解析类型 bool
|
||||||
else if (type == typeof(bool))
|
else if (type == typeof(bool))
|
||||||
{
|
{
|
||||||
return new MemberConditionResolver<bool>
|
return new MemberConditionResolver<bool>
|
||||||
@@ -172,6 +181,8 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
#region 解析类型 string
|
||||||
else if (type == typeof(string))
|
else if (type == typeof(string))
|
||||||
{
|
{
|
||||||
return new MemberStringConditionResolver
|
return new MemberStringConditionResolver
|
||||||
@@ -180,12 +191,22 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
Op = ParseStringOperator(operatorStr),
|
Op = ParseStringOperator(operatorStr),
|
||||||
Value = valueStr
|
Value = valueStr
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
#endregion
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
throw new NotSupportedException($"Type {type} is not supported.");
|
||||||
}
|
}
|
||||||
|
|
||||||
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
|
||||||
|
/// <summary>
|
||||||
|
/// 条件表达式解析
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="data"></param>
|
||||||
|
/// <param name="expression"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
|
/// <exception cref="NotSupportedException"></exception>
|
||||||
|
private static SereinConditionResolver ParseSimpleExpression(object data, string expression)
|
||||||
{
|
{
|
||||||
if ("pass".Equals(expression.ToLower()))
|
if ("pass".Equals(expression.ToLower()))
|
||||||
{
|
{
|
||||||
@@ -252,7 +273,6 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
else if (type == typeof(double))
|
else if (type == typeof(double))
|
||||||
{
|
{
|
||||||
@@ -286,6 +306,14 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 数值操作类型
|
||||||
|
/// </summary>
|
||||||
|
/// <typeparam name="T"></typeparam>
|
||||||
|
/// <param name="operatorStr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
||||||
{
|
{
|
||||||
return operatorStr switch
|
return operatorStr switch
|
||||||
@@ -305,6 +333,12 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 布尔操作类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="operatorStr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
||||||
{
|
{
|
||||||
return operatorStr switch
|
return operatorStr switch
|
||||||
@@ -317,6 +351,12 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 字符串操作类型
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="operatorStr"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
||||||
{
|
{
|
||||||
return operatorStr switch
|
return operatorStr switch
|
||||||
@@ -337,6 +377,6 @@ namespace Serein.NodeFlow.Tool.SerinExpression
|
|||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
12
NodeFlow/Tool/SereinExpression/SereinConditionResolver.cs
Normal file
12
NodeFlow/Tool/SereinExpression/SereinConditionResolver.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using System.Reflection;
|
||||||
|
|
||||||
|
namespace Serein.NodeFlow.Tool.SereinExpression
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 条件解析抽象类
|
||||||
|
/// </summary>
|
||||||
|
public abstract class SereinConditionResolver
|
||||||
|
{
|
||||||
|
public abstract bool Evaluate(object obj);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,14 @@
|
|||||||
using System.Data;
|
using System.Data;
|
||||||
|
|
||||||
namespace Serein.LibraryCore.SerinExpression
|
namespace Serein.NodeFlow.Tool.SereinExpression
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// 使用表达式操作/获取 对象的值
|
||||||
|
/// 获取值 @get .xx.xxx
|
||||||
|
/// 设置值 @set .xx.xxx = [data]
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="obj">操作的对象</param>
|
||||||
|
/// <returns></returns>
|
||||||
public class SerinArithmeticExpressionEvaluator
|
public class SerinArithmeticExpressionEvaluator
|
||||||
{
|
{
|
||||||
private static readonly DataTable table = new DataTable();
|
private static readonly DataTable table = new DataTable();
|
||||||
@@ -30,7 +37,7 @@ namespace Serein.LibraryCore.SerinExpression
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
/// <param name="expression">表达式</param>
|
/// <param name="expression">表达式</param>
|
||||||
/// <param name="targetObJ">操作对象</param>
|
/// <param name="targetObJ">操作对象</param>
|
||||||
/// <param name="isChange">是否改变了对象(get语法)</param>
|
/// <param name="isChange">是否改变了对象(Set语法)</param>
|
||||||
/// <returns></returns>
|
/// <returns></returns>
|
||||||
/// <exception cref="ArgumentException"></exception>
|
/// <exception cref="ArgumentException"></exception>
|
||||||
/// <exception cref="NotSupportedException"></exception>
|
/// <exception cref="NotSupportedException"></exception>
|
||||||
@@ -56,11 +63,11 @@ namespace Serein.LibraryCore.SerinExpression
|
|||||||
|
|
||||||
isChange = operation switch
|
isChange = operation switch
|
||||||
{
|
{
|
||||||
"@num" => true,
|
/*"@num" => true,
|
||||||
"@call" => true,
|
"@call" => true,
|
||||||
"@get" => true,
|
"@get" => true,*/
|
||||||
"@set" => false,
|
"@set" => false,
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
_ => true,
|
||||||
};
|
};
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
@@ -70,6 +77,13 @@ namespace Serein.LibraryCore.SerinExpression
|
|||||||
private static readonly char[] separator = ['(', ')'];
|
private static readonly char[] separator = ['(', ')'];
|
||||||
private static readonly char[] separatorArray = [','];
|
private static readonly char[] separatorArray = [','];
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 调用目标方法
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">目标实例</param>
|
||||||
|
/// <param name="methodCall">方法名称</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static object InvokeMethod(object target, string methodCall)
|
private static object InvokeMethod(object target, string methodCall)
|
||||||
{
|
{
|
||||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
||||||
@@ -98,7 +112,13 @@ namespace Serein.LibraryCore.SerinExpression
|
|||||||
return method.Invoke(target, parameterValues);
|
return method.Invoke(target, parameterValues);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 获取值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">目标实例</param>
|
||||||
|
/// <param name="memberPath">属性路径</param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static object GetMember(object target, string memberPath)
|
private static object GetMember(object target, string memberPath)
|
||||||
{
|
{
|
||||||
var members = memberPath.Split('.');
|
var members = memberPath.Split('.');
|
||||||
@@ -135,7 +155,13 @@ namespace Serein.LibraryCore.SerinExpression
|
|||||||
return target;
|
return target;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// 设置目标的值
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="target">目标实例</param>
|
||||||
|
/// <param name="assignment">属性路径 </param>
|
||||||
|
/// <returns></returns>
|
||||||
|
/// <exception cref="ArgumentException"></exception>
|
||||||
private static object SetMember(object target, string assignment)
|
private static object SetMember(object target, string assignment)
|
||||||
{
|
{
|
||||||
var parts = assignment.Split(new[] { '=' }, 2);
|
var parts = assignment.Split(new[] { '=' }, 2);
|
||||||
@@ -202,6 +228,12 @@ namespace Serein.LibraryCore.SerinExpression
|
|||||||
return target;
|
return target;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// 计算数学简单表达式
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="value"></param>
|
||||||
|
/// <param name="expression"></param>
|
||||||
|
/// <returns></returns>
|
||||||
private static double ComputedNumber(object value, string expression)
|
private static double ComputedNumber(object value, string expression)
|
||||||
{
|
{
|
||||||
double numericValue = Convert.ToDouble(value);
|
double numericValue = Convert.ToDouble(value);
|
||||||
@@ -1,337 +0,0 @@
|
|||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SerinExpression
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 条件解析抽象类
|
|
||||||
/// </summary>
|
|
||||||
public abstract class ConditionResolver
|
|
||||||
{
|
|
||||||
public abstract bool Evaluate(object obj);
|
|
||||||
}
|
|
||||||
|
|
||||||
public class PassConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Pass => true,
|
|
||||||
Operator.NotPass => false,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
Pass,
|
|
||||||
NotPass,
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public class ValueTypeConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 不进行任何操作
|
|
||||||
/// </summary>
|
|
||||||
Node,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于
|
|
||||||
/// </summary>
|
|
||||||
LessThan,
|
|
||||||
/// <summary>
|
|
||||||
/// 等于
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 大于或等于
|
|
||||||
/// </summary>
|
|
||||||
GreaterThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 小于或等于
|
|
||||||
/// </summary>
|
|
||||||
LessThanOrEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 在两者之间
|
|
||||||
/// </summary>
|
|
||||||
InRange,
|
|
||||||
/// <summary>
|
|
||||||
/// 不在两者之间
|
|
||||||
/// </summary>
|
|
||||||
OutOfRange
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
public T RangeStart { get; set; }
|
|
||||||
public T RangeEnd { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is T typedObj)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(typedObj);
|
|
||||||
if (!string.IsNullOrEmpty(ArithmeticExpression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(ArithmeticExpression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
T evaluatedValue = (T)Convert.ChangeType(numericValue, typeof(T));
|
|
||||||
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.GreaterThan => evaluatedValue.CompareTo(Value) > 0,
|
|
||||||
Operator.LessThan => evaluatedValue.CompareTo(Value) < 0,
|
|
||||||
Operator.Equal => evaluatedValue.CompareTo(Value) == 0,
|
|
||||||
Operator.GreaterThanOrEqual => evaluatedValue.CompareTo(Value) >= 0,
|
|
||||||
Operator.LessThanOrEqual => evaluatedValue.CompareTo(Value) <= 0,
|
|
||||||
Operator.InRange => evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0,
|
|
||||||
Operator.OutOfRange => evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0,
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型")
|
|
||||||
};
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.GreaterThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) > 0;
|
|
||||||
case Operator.LessThan:
|
|
||||||
return evaluatedValue.CompareTo(Value) < 0;
|
|
||||||
case Operator.Equal:
|
|
||||||
return evaluatedValue.CompareTo(Value) == 0;
|
|
||||||
case Operator.GreaterThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) >= 0;
|
|
||||||
case Operator.LessThanOrEqual:
|
|
||||||
return evaluatedValue.CompareTo(Value) <= 0;
|
|
||||||
case Operator.InRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) >= 0 && evaluatedValue.CompareTo(RangeEnd) <= 0;
|
|
||||||
case Operator.OutOfRange:
|
|
||||||
return evaluatedValue.CompareTo(RangeStart) < 0 || evaluatedValue.CompareTo(RangeEnd) > 0;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class BoolConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 是
|
|
||||||
/// </summary>
|
|
||||||
Is
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
public bool Value { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj is bool boolObj)
|
|
||||||
{
|
|
||||||
return boolObj == Value;
|
|
||||||
/*switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Is:
|
|
||||||
return boolObj == Value;
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class StringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
public enum Operator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// 出现过
|
|
||||||
/// </summary>
|
|
||||||
Contains,
|
|
||||||
/// <summary>
|
|
||||||
/// 没有出现过
|
|
||||||
/// </summary>
|
|
||||||
DoesNotContain,
|
|
||||||
/// <summary>
|
|
||||||
/// 相等
|
|
||||||
/// </summary>
|
|
||||||
Equal,
|
|
||||||
/// <summary>
|
|
||||||
/// 不相等
|
|
||||||
/// </summary>
|
|
||||||
NotEqual,
|
|
||||||
/// <summary>
|
|
||||||
/// 起始字符串等于
|
|
||||||
/// </summary>
|
|
||||||
StartsWith,
|
|
||||||
/// <summary>
|
|
||||||
/// 结束字符串等于
|
|
||||||
/// </summary>
|
|
||||||
EndsWith
|
|
||||||
}
|
|
||||||
|
|
||||||
public Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
if (obj is string strObj)
|
|
||||||
{
|
|
||||||
return Op switch
|
|
||||||
{
|
|
||||||
Operator.Contains => strObj.Contains(Value),
|
|
||||||
Operator.DoesNotContain => !strObj.Contains(Value),
|
|
||||||
Operator.Equal => strObj == Value,
|
|
||||||
Operator.NotEqual => strObj != Value,
|
|
||||||
Operator.StartsWith => strObj.StartsWith(Value),
|
|
||||||
Operator.EndsWith => strObj.EndsWith(Value),
|
|
||||||
_ => throw new NotSupportedException("不支持的条件类型"),
|
|
||||||
};
|
|
||||||
|
|
||||||
/* switch (Op)
|
|
||||||
{
|
|
||||||
case Operator.Contains:
|
|
||||||
return strObj.Contains(Value);
|
|
||||||
case Operator.DoesNotContain:
|
|
||||||
return !strObj.Contains(Value);
|
|
||||||
case Operator.Equal:
|
|
||||||
return strObj == Value;
|
|
||||||
case Operator.NotEqual:
|
|
||||||
return strObj != Value;
|
|
||||||
case Operator.StartsWith:
|
|
||||||
return strObj.StartsWith(Value);
|
|
||||||
case Operator.EndsWith:
|
|
||||||
return strObj.EndsWith(Value);
|
|
||||||
}*/
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
public class MemberConditionResolver<T> : ConditionResolver where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
//public string MemberPath { get; set; }
|
|
||||||
public ValueTypeConditionResolver<T>.Operator Op { get; set; }
|
|
||||||
public object? TargetObj { get; set; }
|
|
||||||
public T Value { get; set; }
|
|
||||||
|
|
||||||
public string ArithmeticExpression { get; set; }
|
|
||||||
|
|
||||||
public override bool Evaluate(object? obj)
|
|
||||||
{
|
|
||||||
//object? memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (TargetObj is T typedObj)
|
|
||||||
{
|
|
||||||
return new ValueTypeConditionResolver<T>
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value,
|
|
||||||
ArithmeticExpression = ArithmeticExpression,
|
|
||||||
}.Evaluate(typedObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
//private object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
//{
|
|
||||||
// string[] members = memberPath[1..].Split('.');
|
|
||||||
// foreach (var member in members)
|
|
||||||
// {
|
|
||||||
// if (obj == null) return null;
|
|
||||||
// Type type = obj.GetType();
|
|
||||||
// PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
// FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
// if (propertyInfo != null)
|
|
||||||
// obj = propertyInfo.GetValue(obj);
|
|
||||||
// else if (fieldInfo != null)
|
|
||||||
// obj = fieldInfo.GetValue(obj);
|
|
||||||
// else
|
|
||||||
// throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
// }
|
|
||||||
// return obj;
|
|
||||||
//}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class MemberStringConditionResolver : ConditionResolver
|
|
||||||
{
|
|
||||||
|
|
||||||
public string MemberPath { get; set; }
|
|
||||||
|
|
||||||
public StringConditionResolver.Operator Op { get; set; }
|
|
||||||
|
|
||||||
public string Value { get; set; }
|
|
||||||
|
|
||||||
|
|
||||||
public override bool Evaluate(object obj)
|
|
||||||
{
|
|
||||||
object memberValue = GetMemberValue(obj, MemberPath);
|
|
||||||
if (memberValue is string strObj)
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = Op,
|
|
||||||
Value = Value
|
|
||||||
}.Evaluate(strObj);
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
private object GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (obj == null) return null;
|
|
||||||
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
|
|
||||||
return obj;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,341 +0,0 @@
|
|||||||
using System.Globalization;
|
|
||||||
using System.Reflection;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SerinExpression
|
|
||||||
{
|
|
||||||
|
|
||||||
public class SerinConditionParser
|
|
||||||
{
|
|
||||||
public static bool To<T>(T data, string expression)
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
|
|
||||||
return ConditionParse(data, expression).Evaluate(data);
|
|
||||||
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
Console.WriteLine(ex);
|
|
||||||
throw;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static ConditionResolver ConditionParse(object data, string expression)
|
|
||||||
{
|
|
||||||
if (expression.StartsWith('.')) // 表达式前缀属于从上一个节点数据对象获取成员值
|
|
||||||
{
|
|
||||||
return ParseObjectExpression(data, expression);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return ParseSimpleExpression(data, expression);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//bool ContainsArithmeticOperators(string expression)
|
|
||||||
//{
|
|
||||||
// return expression.Contains('+') || expression.Contains('-') || expression.Contains('*') || expression.Contains('/');
|
|
||||||
//}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 获取计算表达式的部分
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="part"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
private static string GetArithmeticExpression(string part)
|
|
||||||
{
|
|
||||||
int startIndex = part.IndexOf('[');
|
|
||||||
int endIndex = part.IndexOf(']');
|
|
||||||
if (startIndex >= 0 && endIndex > startIndex)
|
|
||||||
{
|
|
||||||
return part.Substring(startIndex + 1, endIndex - startIndex - 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 获取对象指定名称的成员
|
|
||||||
/// </summary>
|
|
||||||
private static object? GetMemberValue(object? obj, string memberPath)
|
|
||||||
{
|
|
||||||
string[] members = memberPath[1..].Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
if (obj == null) return null;
|
|
||||||
Type type = obj.GetType();
|
|
||||||
PropertyInfo? propertyInfo = type.GetProperty(member);
|
|
||||||
FieldInfo? fieldInfo = type.GetField(member);
|
|
||||||
if (propertyInfo != null)
|
|
||||||
obj = propertyInfo.GetValue(obj);
|
|
||||||
else if (fieldInfo != null)
|
|
||||||
obj = fieldInfo.GetValue(obj);
|
|
||||||
else
|
|
||||||
throw new ArgumentException($"Member {member} not found in type {type.FullName}");
|
|
||||||
}
|
|
||||||
return obj;
|
|
||||||
}
|
|
||||||
/// <summary>
|
|
||||||
/// 解析对象表达式
|
|
||||||
/// </summary>
|
|
||||||
private static ConditionResolver ParseObjectExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
int typeStartIndex = expression.IndexOf('<');
|
|
||||||
int typeEndIndex = expression.IndexOf('>');
|
|
||||||
|
|
||||||
string memberPath;
|
|
||||||
Type type;
|
|
||||||
object? targetObj;
|
|
||||||
if (typeStartIndex + typeStartIndex == -2)
|
|
||||||
{
|
|
||||||
memberPath = operatorStr;
|
|
||||||
targetObj = GetMemberValue(data, operatorStr);
|
|
||||||
|
|
||||||
type = targetObj.GetType();
|
|
||||||
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (typeStartIndex >= typeEndIndex)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("无效的表达式格式");
|
|
||||||
}
|
|
||||||
memberPath = expression.Substring(0, typeStartIndex).Trim();
|
|
||||||
string typeStr = expression.Substring(typeStartIndex + 1, typeEndIndex - typeStartIndex - 1).Trim().ToLower();
|
|
||||||
parts = expression.Substring(typeEndIndex + 1).Trim().Split(' ');
|
|
||||||
if (parts.Length == 3)
|
|
||||||
{
|
|
||||||
operatorStr = parts[1].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(2));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
operatorStr = parts[0].ToLower();
|
|
||||||
valueStr = string.Join(' ', parts.Skip(1));
|
|
||||||
}
|
|
||||||
targetObj = GetMemberValue(data, memberPath);
|
|
||||||
|
|
||||||
Type? tempType = typeStr switch
|
|
||||||
{
|
|
||||||
"int" => typeof(int),
|
|
||||||
"double" => typeof(double),
|
|
||||||
"bool" => typeof(bool),
|
|
||||||
"string" => typeof(string),
|
|
||||||
_ => Type.GetType(typeStr)
|
|
||||||
};
|
|
||||||
type = tempType ?? throw new ArgumentException("对象表达式无效的类型声明");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<int>
|
|
||||||
{
|
|
||||||
TargetObj = targetObj,
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
Op = ParseValueTypeOperator<int>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new MemberConditionResolver<double>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
return new MemberConditionResolver<bool>
|
|
||||||
{
|
|
||||||
//MemberPath = memberPath,
|
|
||||||
TargetObj = targetObj,
|
|
||||||
Op = (ValueTypeConditionResolver<bool>.Operator)ParseBoolOperator(operatorStr)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new MemberStringConditionResolver
|
|
||||||
{
|
|
||||||
MemberPath = memberPath,
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
private static ConditionResolver ParseSimpleExpression(object data, string expression)
|
|
||||||
{
|
|
||||||
if ("pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.Pass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if ("not pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
if ("!pass".Equals(expression.ToLower()))
|
|
||||||
{
|
|
||||||
return new PassConditionResolver
|
|
||||||
{
|
|
||||||
Op = PassConditionResolver.Operator.NotPass,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
var parts = expression.Split(' ');
|
|
||||||
|
|
||||||
if (parts.Length < 2)
|
|
||||||
throw new ArgumentException("无效的表达式格式。");
|
|
||||||
|
|
||||||
//string typeStr = parts[0];
|
|
||||||
string operatorStr = parts[0];
|
|
||||||
string valueStr = string.Join(' ', parts, 1, parts.Length - 1);
|
|
||||||
|
|
||||||
Type type = data.GetType();//Type.GetType(typeStr);
|
|
||||||
if (type == typeof(int))
|
|
||||||
{
|
|
||||||
var op = ParseValueTypeOperator<int>(operatorStr);
|
|
||||||
if (op == ValueTypeConditionResolver<int>.Operator.InRange || op == ValueTypeConditionResolver<int>.Operator.OutOfRange)
|
|
||||||
{
|
|
||||||
var temp = valueStr.Split('-');
|
|
||||||
if (temp.Length < 2)
|
|
||||||
throw new ArgumentException($"范围无效:{valueStr}。");
|
|
||||||
int rangeStart = int.Parse(temp[0], CultureInfo.InvariantCulture);
|
|
||||||
int rangeEnd = int.Parse(temp[1], CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
RangeStart = rangeStart,
|
|
||||||
RangeEnd = rangeEnd,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0]),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
int value = int.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<int>
|
|
||||||
{
|
|
||||||
Op = op,
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else if (type == typeof(double))
|
|
||||||
{
|
|
||||||
double value = double.Parse(valueStr, CultureInfo.InvariantCulture);
|
|
||||||
return new ValueTypeConditionResolver<double>
|
|
||||||
{
|
|
||||||
Op = ParseValueTypeOperator<double>(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
ArithmeticExpression = GetArithmeticExpression(parts[0])
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(bool))
|
|
||||||
{
|
|
||||||
bool value = bool.Parse(valueStr);
|
|
||||||
return new BoolConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseBoolOperator(operatorStr),
|
|
||||||
Value = value,
|
|
||||||
};
|
|
||||||
}
|
|
||||||
else if (type == typeof(string))
|
|
||||||
{
|
|
||||||
return new StringConditionResolver
|
|
||||||
{
|
|
||||||
Op = ParseStringOperator(operatorStr),
|
|
||||||
Value = valueStr
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
throw new NotSupportedException($"Type {type} is not supported.");
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static ValueTypeConditionResolver<T>.Operator ParseValueTypeOperator<T>(string operatorStr) where T : struct, IComparable<T>
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
">" => ValueTypeConditionResolver<T>.Operator.GreaterThan,
|
|
||||||
"<" => ValueTypeConditionResolver<T>.Operator.LessThan,
|
|
||||||
"=" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
"==" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
">=" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"≥" => ValueTypeConditionResolver<T>.Operator.GreaterThanOrEqual,
|
|
||||||
"<=" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"≤" => ValueTypeConditionResolver<T>.Operator.LessThanOrEqual,
|
|
||||||
"equals" => ValueTypeConditionResolver<T>.Operator.Equal,
|
|
||||||
"in" => ValueTypeConditionResolver<T>.Operator.InRange,
|
|
||||||
"!in" => ValueTypeConditionResolver<T>.Operator.OutOfRange,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for value type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static BoolConditionResolver.Operator ParseBoolOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"is" => BoolConditionResolver.Operator.Is,
|
|
||||||
"==" => BoolConditionResolver.Operator.Is,
|
|
||||||
"equals" => BoolConditionResolver.Operator.Is,
|
|
||||||
//"isFalse" => BoolConditionNode.Operator.IsFalse,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for bool type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
private static StringConditionResolver.Operator ParseStringOperator(string operatorStr)
|
|
||||||
{
|
|
||||||
return operatorStr switch
|
|
||||||
{
|
|
||||||
"c" => StringConditionResolver.Operator.Contains,
|
|
||||||
"nc" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"sw" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"ew" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
|
|
||||||
"contains" => StringConditionResolver.Operator.Contains,
|
|
||||||
"doesNotContain" => StringConditionResolver.Operator.DoesNotContain,
|
|
||||||
"equals" => StringConditionResolver.Operator.Equal,
|
|
||||||
"==" => StringConditionResolver.Operator.Equal,
|
|
||||||
"notEquals" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"!=" => StringConditionResolver.Operator.NotEqual,
|
|
||||||
"startsWith" => StringConditionResolver.Operator.StartsWith,
|
|
||||||
"endsWith" => StringConditionResolver.Operator.EndsWith,
|
|
||||||
_ => throw new ArgumentException($"Invalid operator {operatorStr} for string type.")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,216 +0,0 @@
|
|||||||
using System.Data;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool.SerinExpression
|
|
||||||
{
|
|
||||||
public class SerinArithmeticExpressionEvaluator
|
|
||||||
{
|
|
||||||
private static readonly DataTable table = new DataTable();
|
|
||||||
|
|
||||||
public static double Evaluate(string expression, double inputValue)
|
|
||||||
{
|
|
||||||
// 替换占位符@为输入值
|
|
||||||
expression = expression.Replace("@", inputValue.ToString());
|
|
||||||
try
|
|
||||||
{
|
|
||||||
// 使用 DataTable.Compute 方法计算表达式
|
|
||||||
var result = table.Compute(expression, string.Empty);
|
|
||||||
return Convert.ToDouble(result);
|
|
||||||
}
|
|
||||||
catch
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid arithmetic expression.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class SerinExpressionEvaluator
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
///
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="expression">表达式</param>
|
|
||||||
/// <param name="targetObJ">操作对象</param>
|
|
||||||
/// <param name="isChange">是否改变了对象(get语法)</param>
|
|
||||||
/// <returns></returns>
|
|
||||||
/// <exception cref="ArgumentException"></exception>
|
|
||||||
/// <exception cref="NotSupportedException"></exception>
|
|
||||||
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
|
||||||
{
|
|
||||||
var parts = expression.Split([' '], 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid expression format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var operation = parts[0].ToLower();
|
|
||||||
var operand = parts[1][0] == '.' ? parts[1][1..] : parts[1];
|
|
||||||
|
|
||||||
var result = operation switch
|
|
||||||
{
|
|
||||||
"@num" => ComputedNumber(targetObJ, operand),
|
|
||||||
"@call" => InvokeMethod(targetObJ, operand),
|
|
||||||
"@get" => GetMember(targetObJ, operand),
|
|
||||||
"@set" => SetMember(targetObJ, operand),
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
isChange = operation switch
|
|
||||||
{
|
|
||||||
"@num" => true,
|
|
||||||
"@call" => true,
|
|
||||||
"@get" => true,
|
|
||||||
"@set" => false,
|
|
||||||
_ => throw new NotSupportedException($"Operation {operation} is not supported.")
|
|
||||||
};
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private static readonly char[] separator = ['(', ')'];
|
|
||||||
private static readonly char[] separatorArray = [','];
|
|
||||||
|
|
||||||
private static object InvokeMethod(object target, string methodCall)
|
|
||||||
{
|
|
||||||
var methodParts = methodCall.Split(separator, StringSplitOptions.RemoveEmptyEntries);
|
|
||||||
if (methodParts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid method call format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var methodName = methodParts[0];
|
|
||||||
var parameterList = methodParts[1];
|
|
||||||
var parameters = parameterList.Split(separatorArray, StringSplitOptions.RemoveEmptyEntries)
|
|
||||||
.Select(p => p.Trim())
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
var method = target.GetType().GetMethod(methodName);
|
|
||||||
if (method == null)
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Method {methodName} not found on target.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var parameterValues = method.GetParameters()
|
|
||||||
.Select((p, index) => Convert.ChangeType(parameters[index], p.ParameterType))
|
|
||||||
.ToArray();
|
|
||||||
|
|
||||||
|
|
||||||
return method.Invoke(target, parameterValues);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object GetMember(object target, string memberPath)
|
|
||||||
{
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
foreach (var member in members)
|
|
||||||
{
|
|
||||||
|
|
||||||
if (target == null) return null;
|
|
||||||
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
return target;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private static object SetMember(object target, string assignment)
|
|
||||||
{
|
|
||||||
var parts = assignment.Split(new[] { '=' }, 2);
|
|
||||||
if (parts.Length != 2)
|
|
||||||
{
|
|
||||||
throw new ArgumentException("Invalid assignment format.");
|
|
||||||
}
|
|
||||||
|
|
||||||
var memberPath = parts[0].Trim();
|
|
||||||
var value = parts[1].Trim();
|
|
||||||
|
|
||||||
var members = memberPath.Split('.');
|
|
||||||
for (int i = 0; i < members.Length - 1; i++)
|
|
||||||
{
|
|
||||||
var member = members[i];
|
|
||||||
|
|
||||||
var property = target.GetType().GetProperty(member);
|
|
||||||
|
|
||||||
if (property != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = property.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var field = target.GetType().GetField(member);
|
|
||||||
if (field != null)
|
|
||||||
{
|
|
||||||
|
|
||||||
target = field.GetValue(target);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {member} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var lastMember = members.Last();
|
|
||||||
|
|
||||||
var lastProperty = target.GetType().GetProperty(lastMember);
|
|
||||||
|
|
||||||
if (lastProperty != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastProperty.PropertyType);
|
|
||||||
lastProperty.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
var lastField = target.GetType().GetField(lastMember);
|
|
||||||
if (lastField != null)
|
|
||||||
{
|
|
||||||
var convertedValue = Convert.ChangeType(value, lastField.FieldType);
|
|
||||||
lastField.SetValue(target, convertedValue);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
throw new ArgumentException($"Member {lastMember} not found on target.");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return target;
|
|
||||||
}
|
|
||||||
|
|
||||||
private static double ComputedNumber(object value, string expression)
|
|
||||||
{
|
|
||||||
double numericValue = Convert.ToDouble(value);
|
|
||||||
if (!string.IsNullOrEmpty(expression))
|
|
||||||
{
|
|
||||||
numericValue = SerinArithmeticExpressionEvaluator.Evaluate(expression, numericValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
return numericValue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
using System.Collections.Concurrent;
|
|
||||||
using Serein.NodeFlow;
|
|
||||||
using Serein.NodeFlow.Model;
|
|
||||||
|
|
||||||
namespace Serein.NodeFlow.Tool
|
|
||||||
{
|
|
||||||
public class TcsSignalException : Exception
|
|
||||||
{
|
|
||||||
public FlowStateType FsState { get; set; }
|
|
||||||
public TcsSignalException(string? message) : base(message)
|
|
||||||
{
|
|
||||||
FsState = FlowStateType.Error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public class TcsSignal<TSignal> where TSignal : struct, Enum
|
|
||||||
{
|
|
||||||
//public ConcurrentDictionary<TSignal, Queue<TaskCompletionSource<object>>> TcsEvent { get; } = new();
|
|
||||||
public ConcurrentDictionary<TSignal, TaskCompletionSource<object>> TcsEvent { get; } = new();
|
|
||||||
|
|
||||||
public ConcurrentDictionary<TSignal, object> TcsLock { get; } = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 触发信号
|
|
||||||
/// </summary>
|
|
||||||
/// <typeparam name="T"></typeparam>
|
|
||||||
/// <param name="signal">信号</param>
|
|
||||||
/// <param name="value">传递的参数</param>
|
|
||||||
/// <returns>是否成功触发</returns>
|
|
||||||
public bool TriggerSignal<T>(TSignal signal, T value)
|
|
||||||
{
|
|
||||||
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
|
||||||
lock (tcsLock)
|
|
||||||
{
|
|
||||||
if (TcsEvent.TryRemove(signal, out var waitTcs))
|
|
||||||
{
|
|
||||||
waitTcs.SetResult(value);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public TaskCompletionSource<object> CreateTcs(TSignal signal)
|
|
||||||
{
|
|
||||||
var tcsLock = TcsLock.GetOrAdd(signal, new object());
|
|
||||||
lock (tcsLock)
|
|
||||||
{
|
|
||||||
var tcs = TcsEvent.GetOrAdd(signal, new TaskCompletionSource<object>());
|
|
||||||
return tcs;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CancelTask()
|
|
||||||
{
|
|
||||||
foreach (var tcs in TcsEvent.Values)
|
|
||||||
{
|
|
||||||
tcs.SetException(new TcsSignalException("任务取消"));
|
|
||||||
}
|
|
||||||
TcsEvent.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user