mirror of
https://gitee.com/langsisi_admin/serein-flow
synced 2026-03-03 00:00:49 +08:00
在Serein.Library添加了基础功能模块,暂时实现了键值对/数组数据的创建(可配合JSON库进行序列化)
This commit is contained in:
@@ -31,6 +31,11 @@ namespace Serein.Library.Api
|
||||
/// </summary>
|
||||
ConnectionInvokeType NextOrientation { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 运行时异常信息
|
||||
/// </summary>
|
||||
Exception ExceptionOfRuning { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 设置节点的运行时上一节点,用以多线程中隔开不同流程的数据
|
||||
/// </summary>
|
||||
|
||||
@@ -857,18 +857,20 @@ namespace Serein.Library.Api
|
||||
/// <summary>
|
||||
/// 获取方法描述信息
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集名称</param>
|
||||
/// <param name="methodName">方法描述</param>
|
||||
/// <param name="mdInfo">方法信息</param>
|
||||
/// <returns></returns>
|
||||
bool TryGetMethodDetailsInfo(string libraryName, string methodName, out MethodDetailsInfo mdInfo);
|
||||
bool TryGetMethodDetailsInfo(string assemblyName, string methodName, out MethodDetailsInfo mdInfo);
|
||||
|
||||
/// <summary>
|
||||
/// 获取指定方法的Emit委托
|
||||
/// </summary>
|
||||
/// <param name="assemblyName">程序集名称</param>
|
||||
/// <param name="methodName"></param>
|
||||
/// <param name="del"></param>
|
||||
/// <returns></returns>
|
||||
bool TryGetDelegateDetails(string libraryName, string methodName, out DelegateDetails del);
|
||||
bool TryGetDelegateDetails(string assemblyName, string methodName, out DelegateDetails del);
|
||||
|
||||
|
||||
#region 远程相关
|
||||
|
||||
@@ -26,6 +26,7 @@ namespace Serein.Library
|
||||
_emitDelegate = emitDelegate;
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 记录Emit委托
|
||||
/// </summary>
|
||||
@@ -36,7 +37,9 @@ namespace Serein.Library
|
||||
_emitMethodType = EmitMethodType;
|
||||
_emitDelegate = EmitDelegate;
|
||||
}
|
||||
/// <summary>
|
||||
|
||||
|
||||
/*/// <summary>
|
||||
/// 更新委托方法
|
||||
/// </summary>
|
||||
/// <param name="EmitMethodType"></param>
|
||||
@@ -45,7 +48,9 @@ namespace Serein.Library
|
||||
{
|
||||
_emitMethodType = EmitMethodType;
|
||||
_emitDelegate = EmitDelegate;
|
||||
}
|
||||
}*/
|
||||
|
||||
|
||||
private Delegate _emitDelegate;
|
||||
private EmitMethodType _emitMethodType;
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
@@ -36,7 +37,7 @@ namespace Serein.Library
|
||||
private bool _isProtectionParameter;
|
||||
|
||||
/// <summary>
|
||||
/// 作用实例的类型(多个相同的节点将拥有相同的类型)
|
||||
/// 调用节点方法时需要的实例(多个相同的节点将拥有相同的类型)
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private Type _actingInstanceType;
|
||||
@@ -77,6 +78,7 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private ParameterDetails[] _parameterDetailss;
|
||||
//private List<ParameterDetails> _parameterDetailss;
|
||||
|
||||
/// <summary>
|
||||
/// <para>描述该方法是否存在可选参数</para>
|
||||
@@ -137,6 +139,9 @@ namespace Serein.Library
|
||||
&& index < ParameterDetailss.Length) // 防止下标越界
|
||||
{
|
||||
ParameterDetailss[index] = null; // 释放对象引用
|
||||
|
||||
|
||||
|
||||
var tmp = ArrayHelper.RemoteToArray<ParameterDetails>(ParameterDetailss, index); // 新增;
|
||||
UpdateParamIndex(ref tmp);
|
||||
ParameterDetailss = tmp; // 新增
|
||||
|
||||
@@ -62,25 +62,6 @@ namespace Serein.Library
|
||||
/// </summary>
|
||||
[PropertyInfo(IsProtection = true)]
|
||||
private MethodDetails _methodDetails ;
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的上一节点
|
||||
/// </summary>
|
||||
//[PropertyInfo]
|
||||
//private NodeModelBase _previousNode ;
|
||||
|
||||
/// <summary>
|
||||
/// 当前节点执行完毕后需要执行的下一个分支的类别
|
||||
/// </summary>
|
||||
//[PropertyInfo]
|
||||
//private ConnectionInvokeType _nextOrientation = ConnectionInvokeType.None;
|
||||
|
||||
/// <summary>
|
||||
/// 运行时的异常信息(仅在 FlowState 为 Error 时存在对应值)
|
||||
/// </summary>
|
||||
[PropertyInfo]
|
||||
private Exception _runingException ;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -11,6 +11,7 @@ using System.Linq;
|
||||
using System.Linq.Expressions;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Reflection;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -109,7 +110,7 @@ namespace Serein.Library
|
||||
{
|
||||
// 保存的参数信息项数量大于方法本身的方法入参数量(可能存在可变入参)
|
||||
var length = nodeInfo.ParameterData.Length - pds.Length; // 需要扩容的长度
|
||||
this.MethodDetails.ParameterDetailss = ArrayHelper.ArrayExpansion(pds, length); // 扩容入参描述数组
|
||||
this.MethodDetails.ParameterDetailss = ArrayHelper.Expansion(pds, length); // 扩容入参描述数组
|
||||
pds = this.MethodDetails.ParameterDetailss;
|
||||
var startParmsPd = pds[md.ParamsArgIndex]; // 获取可变入参参数描述
|
||||
for(int i = md.ParamsArgIndex + 1; i <= md.ParamsArgIndex + length; i++)
|
||||
@@ -144,6 +145,10 @@ namespace Serein.Library
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region 程序集更新,更新节点方法描述、以及所有入参描述的类型
|
||||
|
||||
#endregion
|
||||
|
||||
#region 调试中断
|
||||
|
||||
/// <summary>
|
||||
@@ -256,7 +261,6 @@ namespace Serein.Library
|
||||
newFlowData = null;
|
||||
await Console.Out.WriteLineAsync($"节点[{this.MethodDetails?.MethodName}]异常:" + ex);
|
||||
context.NextOrientation = ConnectionInvokeType.IsError;
|
||||
currentNode.RuningException = ex;
|
||||
}
|
||||
|
||||
|
||||
@@ -317,7 +321,7 @@ namespace Serein.Library
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在方法信息,请检查是否需要重写节点的ExecutingAsync");
|
||||
}
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd))
|
||||
if (!context.Env.TryGetDelegateDetails(md.AssemblyName, md.MethodName, out var dd)) // 流程运行到某个节点
|
||||
{
|
||||
throw new Exception($"节点{this.Guid}不存在对应委托");
|
||||
}
|
||||
@@ -700,4 +704,68 @@ namespace Serein.Library
|
||||
#endregion
|
||||
|
||||
}
|
||||
|
||||
|
||||
#if false
|
||||
public static class NodeModelExtension
|
||||
{
|
||||
/// <summary>
|
||||
/// 程序集更新,更新节点方法描述、以及所有入参描述的类型
|
||||
/// </summary>
|
||||
/// <param name="nodeModel">节点Model</param>
|
||||
/// <param name="newMd">新的方法描述</param>
|
||||
public static void UploadMethod(this NodeModelBase nodeModel, MethodDetails newMd)
|
||||
{
|
||||
var thisMd = nodeModel.MethodDetails;
|
||||
|
||||
thisMd.ActingInstanceType = newMd.ActingInstanceType; // 更新方法需要的类型
|
||||
|
||||
var thisPds = thisMd.ParameterDetailss;
|
||||
var newPds = newMd.ParameterDetailss;
|
||||
// 当前存在可变参数,且新的方法也存在可变参数,需要把可变参数的数目与值传递过去
|
||||
if (thisMd.HasParamsArg && newMd.HasParamsArg)
|
||||
{
|
||||
int paramsLength = thisPds.Length - thisMd.ParamsArgIndex - 1; // 确定扩容长度
|
||||
newMd.ParameterDetailss = ArrayHelper.Expansion(newPds, paramsLength);// 为新方法的入参参数描述进行扩容
|
||||
newPds = newMd.ParameterDetailss;
|
||||
int index = newMd.ParamsArgIndex; // 记录
|
||||
var templatePd = newPds[newMd.ParamsArgIndex]; // 新的入参模板
|
||||
for (int i = thisMd.ParamsArgIndex; i < thisPds.Length; i++)
|
||||
{
|
||||
ParameterDetails thisPd = thisPds[i];
|
||||
var newPd = templatePd.CloneOfModel(nodeModel); // 复制参数描述
|
||||
newPd.Index = i + 1; // 更新索引
|
||||
newPd.IsParams = true;
|
||||
newPd.DataValue = thisPd.DataValue; // 保留参数值
|
||||
newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息
|
||||
newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息
|
||||
newPd.IsParams = thisPd.IsParams; // 保留显式参数设置
|
||||
newPds[index++] = newPd;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
var thidPdLength = thisMd.HasParamsArg ? thisMd.ParamsArgIndex : thisPds.Length;
|
||||
// 遍历当前的参数描述(不包含可变参数),找到匹配项,复制必要的数据进行保留
|
||||
for (int i = 0; i < thisPds.Length; i++)
|
||||
{
|
||||
ParameterDetails thisPd = thisPds[i];
|
||||
var newPd = newPds.FirstOrDefault(t_newPd => !t_newPd.IsParams // 不为可变参数
|
||||
&& t_newPd.Name.Equals(thisPd.Name, StringComparison.OrdinalIgnoreCase) // 存在相同名称
|
||||
&& t_newPd.DataType.Name.Equals(thisPd.DataType.Name) // 存在相同入参类型名称(以类型作为区分)
|
||||
);
|
||||
if (newPd != null) // 如果匹配上了
|
||||
{
|
||||
newPd.DataValue = thisPd.DataValue; // 保留参数值
|
||||
newPd.ArgDataSourceNodeGuid = thisPd.ArgDataSourceNodeGuid; // 保留参数来源信息
|
||||
newPd.ArgDataSourceType = thisPd.ArgDataSourceType; // 保留参数来源信息
|
||||
newPd.IsParams = thisPd.IsParams; // 保留显式参数设置
|
||||
}
|
||||
}
|
||||
thisMd.ReturnType = newMd.ReturnType;
|
||||
nodeModel.MethodDetails = newMd;
|
||||
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
108
Library/SereinBaseFunction.cs
Normal file
108
Library/SereinBaseFunction.cs
Normal file
@@ -0,0 +1,108 @@
|
||||
using Serein.Library;
|
||||
using Serein.Library.Api;
|
||||
using Serein.Library.Utils;
|
||||
using Serein.Library.Utils.SereinExpression;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Dynamic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Serein.Library
|
||||
{
|
||||
/// <summary>
|
||||
/// 基础功能
|
||||
/// </summary>
|
||||
|
||||
[DynamicFlow(Name ="[基础功能]")]
|
||||
public class SereinBaseFunction
|
||||
{
|
||||
//[NodeAction(NodeType.Action,"条件节点")]
|
||||
//private bool SereinConditionNode(IDynamicContext context,
|
||||
// object targetObject,
|
||||
// string exp = "ISPASS")
|
||||
//{
|
||||
// var isPass = SereinConditionParser.To(targetObject, exp);
|
||||
// context.NextOrientation = isPass ? ConnectionInvokeType.IsSucceed : ConnectionInvokeType.IsFail;
|
||||
// return isPass;
|
||||
//}
|
||||
|
||||
//[NodeAction(NodeType.Action, "表达式节点")]
|
||||
//private object SereinExpNode(IDynamicContext context,
|
||||
// object targetObject,
|
||||
// string exp)
|
||||
//{
|
||||
|
||||
// exp = "@" + exp;
|
||||
// var newData = SerinExpressionEvaluator.Evaluate(exp, targetObject, out bool isChange);
|
||||
// object result;
|
||||
// if (isChange || exp.StartsWith("@GET",System.StringComparison.OrdinalIgnoreCase))
|
||||
// {
|
||||
// result = newData;
|
||||
// }
|
||||
// else
|
||||
// {
|
||||
// result = targetObject;
|
||||
// }
|
||||
// context.NextOrientation = ConnectionInvokeType.IsSucceed;
|
||||
// return result;
|
||||
//}
|
||||
|
||||
|
||||
|
||||
[NodeAction(NodeType.Action, "键值对组装")]
|
||||
private Dictionary<string, object> SereinKvDataCollectionNode(/*NodeModelBase nodeModel, */
|
||||
string argName,
|
||||
params object[] value)
|
||||
{
|
||||
//var paramsArgIndex = nodeModel.MethodDetails.ParamsArgIndex;
|
||||
//var pds = nodeModel.MethodDetails.ParameterDetailss;
|
||||
//var length = pds.Length - paramsArgIndex;
|
||||
//for(int i = paramsArgIndex; i < pds.Length; i++)
|
||||
//{
|
||||
// var pd = pds[i];
|
||||
//}
|
||||
|
||||
var names = argName.Split(';');
|
||||
var count = Math.Min(value.Length, names.Length);
|
||||
var dict = new Dictionary<string, object>();
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
dict[names[i]] = value[i];
|
||||
}
|
||||
return dict;
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "数组组装")]
|
||||
private object[] SereinListDataCollectionNode(params object[] value)
|
||||
{
|
||||
return value;
|
||||
}
|
||||
|
||||
[NodeAction(NodeType.Action, "输出")]
|
||||
private object[] SereinConsoleNode(params object[] value)
|
||||
{
|
||||
foreach (var item in value)
|
||||
{
|
||||
Console.WriteLine(item);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
/* if (!DynamicObjectHelper.TryResolve(dict, className, out var result))
|
||||
{
|
||||
Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
}
|
||||
else
|
||||
{
|
||||
DynamicObjectHelper.PrintObjectProperties(result);
|
||||
}
|
||||
//if (!ObjDynamicCreateHelper.TryResolve(externalData, "RootType", out var result))
|
||||
//{
|
||||
// Console.WriteLine("赋值过程中有错误,请检查属性名和类型!");
|
||||
|
||||
//}
|
||||
//ObjDynamicCreateHelper.PrintObjectProperties(result!);
|
||||
return result;*/
|
||||
}
|
||||
}
|
||||
@@ -19,7 +19,7 @@ namespace Serein.Library.Utils
|
||||
/// <param name="length">扩容长度</param>
|
||||
/// <returns>新的数组</returns>
|
||||
/// <exception cref="Exception"> length 传入负值</exception>
|
||||
public static T[] ArrayExpansion<T>(T[] original, int length)
|
||||
public static T[] Expansion<T>(T[] original, int length)
|
||||
{
|
||||
if(length == 0)
|
||||
{
|
||||
@@ -58,7 +58,10 @@ namespace Serein.Library.Utils
|
||||
public static T[] AddToArray<T>(T[] original, T newObject)
|
||||
{
|
||||
// 创建一个新数组,比原数组大1
|
||||
T[] newArray = ArrayHelper.ArrayExpansion(original, 1);
|
||||
T[] newArray = ArrayHelper.Expansion(original, 1);
|
||||
|
||||
original.CopyTo(newArray, 0);
|
||||
|
||||
// 将新对象放在最后一位
|
||||
newArray[newArray.Length - 1] = newObject;
|
||||
return newArray;
|
||||
|
||||
303
Library/Utils/DynamicObjectHelper.cs
Normal file
303
Library/Utils/DynamicObjectHelper.cs
Normal file
@@ -0,0 +1,303 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Reflection.Emit;
|
||||
using System.Reflection;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Serein.Library.Utils
|
||||
{
|
||||
public class DynamicObjectHelper
|
||||
{
|
||||
// 类型缓存,键为类型的唯一名称(可以根据实际需求调整生成方式)
|
||||
static Dictionary<string, Type> typeCache = new Dictionary<string, Type>();
|
||||
|
||||
public static object Resolve(IDictionary<string, object> properties, string typeName)
|
||||
{
|
||||
var obj = CreateObjectWithProperties(properties, typeName);
|
||||
//SetPropertyValues(obj, properties);
|
||||
return obj;
|
||||
}
|
||||
public static bool TryResolve(IDictionary<string, object> properties, string typeName, out object result)
|
||||
{
|
||||
result = CreateObjectWithProperties(properties, typeName);
|
||||
bool success = SetPropertyValuesWithValidation(result, properties);
|
||||
return success;
|
||||
// 打印赋值结果
|
||||
|
||||
}
|
||||
// 递归方法:打印对象属性及类型
|
||||
public static void PrintObjectProperties(object obj, string indent = "")
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
foreach (var prop in objType.GetProperties())
|
||||
{
|
||||
var value = prop.GetValue(obj);
|
||||
Console.WriteLine($"{indent}{prop.Name} (Type: {prop.PropertyType.Name}): {value}");
|
||||
|
||||
if (value != null)
|
||||
{
|
||||
if (prop.PropertyType.IsArray) // 处理数组类型
|
||||
{
|
||||
var array = (Array)value;
|
||||
Console.WriteLine($"{indent}{prop.Name} is an array with {array.Length} elements:");
|
||||
for (int i = 0; i < array.Length; i++)
|
||||
{
|
||||
var element = array.GetValue(i);
|
||||
if (element != null && element.GetType().IsClass && !(element is string))
|
||||
{
|
||||
Console.WriteLine($"{indent}\tArray[{i}] (Type: {element.GetType().Name}) contains a nested object:");
|
||||
PrintObjectProperties(element, indent + "\t\t");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($"{indent}\tArray[{i}] (Type: {element?.GetType().Name}): {element}");
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (value.GetType().IsClass && !(value is string)) // 处理嵌套对象
|
||||
{
|
||||
Console.WriteLine($"{indent}{prop.Name} contains a nested object:");
|
||||
PrintObjectProperties(value, indent + "\t");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 方法 1: 创建动态类型及其对象实例
|
||||
public static object CreateObjectWithProperties(IDictionary<string, object> properties, string typeName)
|
||||
{
|
||||
// 如果类型已经缓存,直接返回缓存的类型
|
||||
if (typeCache.ContainsKey(typeName))
|
||||
{
|
||||
return Activator.CreateInstance(typeCache[typeName]);
|
||||
}
|
||||
|
||||
// 定义动态程序集和模块
|
||||
var assemblyName = new AssemblyName("DynamicAssembly");
|
||||
var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(assemblyName, AssemblyBuilderAccess.Run);
|
||||
var moduleBuilder = assemblyBuilder.DefineDynamicModule("MainModule");
|
||||
|
||||
// 定义动态类型
|
||||
var typeBuilder = moduleBuilder.DefineType(typeName, TypeAttributes.Public);
|
||||
|
||||
// 为每个属性名和值添加相应的属性到动态类型中
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
string propName = kvp.Key;
|
||||
object propValue = kvp.Value;
|
||||
Type propType;
|
||||
|
||||
if (propValue is IList<Dictionary<string, object>>) // 处理数组类型
|
||||
{
|
||||
var nestedPropValue = (propValue as IList<Dictionary<string, object>>)[0];
|
||||
var nestedType = CreateObjectWithProperties(nestedPropValue, $"{propName}Element");
|
||||
propType = nestedType.GetType().MakeArrayType(); // 创建数组类型
|
||||
}
|
||||
else if (propValue is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
// 如果值是嵌套的字典,递归创建嵌套类型
|
||||
propType = CreateObjectWithProperties(nestedProperties, $"{typeName}_{propName}").GetType();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 如果是普通类型,使用值的类型
|
||||
propType = propValue?.GetType() ?? typeof(object);
|
||||
}
|
||||
|
||||
// 定义私有字段和公共属性
|
||||
var fieldBuilder = typeBuilder.DefineField("_" + propName, propType, FieldAttributes.Private);
|
||||
var propertyBuilder = typeBuilder.DefineProperty(propName, PropertyAttributes.HasDefault, propType, null);
|
||||
|
||||
// 定义 getter 方法
|
||||
var getMethodBuilder = typeBuilder.DefineMethod(
|
||||
"get_" + propName,
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
propType,
|
||||
Type.EmptyTypes);
|
||||
|
||||
var getIL = getMethodBuilder.GetILGenerator();
|
||||
getIL.Emit(OpCodes.Ldarg_0);
|
||||
getIL.Emit(OpCodes.Ldfld, fieldBuilder);
|
||||
getIL.Emit(OpCodes.Ret);
|
||||
|
||||
// 定义 setter 方法
|
||||
var setMethodBuilder = typeBuilder.DefineMethod(
|
||||
"set_" + propName,
|
||||
MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.HideBySig,
|
||||
null,
|
||||
new Type[] { propType });
|
||||
|
||||
var setIL = setMethodBuilder.GetILGenerator();
|
||||
setIL.Emit(OpCodes.Ldarg_0);
|
||||
setIL.Emit(OpCodes.Ldarg_1);
|
||||
setIL.Emit(OpCodes.Stfld, fieldBuilder);
|
||||
setIL.Emit(OpCodes.Ret);
|
||||
|
||||
// 将 getter 和 setter 方法添加到属性
|
||||
propertyBuilder.SetGetMethod(getMethodBuilder);
|
||||
propertyBuilder.SetSetMethod(setMethodBuilder);
|
||||
}
|
||||
|
||||
// 创建类型并缓存
|
||||
var dynamicType = typeBuilder.CreateType();
|
||||
typeCache[typeName] = dynamicType;
|
||||
|
||||
// 创建对象实例
|
||||
return Activator.CreateInstance(dynamicType);
|
||||
}
|
||||
|
||||
// 方法 2: 递归设置对象的属性值
|
||||
public static void SetPropertyValues(object obj, Dictionary<string, object> properties)
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
var propInfo = objType.GetProperty(kvp.Key);
|
||||
object value = kvp.Value;
|
||||
|
||||
// 如果值是嵌套的字典类型,递归处理嵌套对象
|
||||
if (value is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
// 创建嵌套对象
|
||||
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
|
||||
|
||||
// 递归设置嵌套对象的值
|
||||
SetPropertyValues(nestedObj, nestedProperties);
|
||||
|
||||
// 将嵌套对象赋值给属性
|
||||
propInfo.SetValue(obj, nestedObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
// 直接赋值给属性
|
||||
propInfo.SetValue(obj, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 方法 2: 递归设置对象的属性值(带验证)
|
||||
|
||||
public static bool SetPropertyValuesWithValidation(object obj, IDictionary<string, object> properties)
|
||||
{
|
||||
var objType = obj.GetType();
|
||||
bool allSuccessful = true; // 标记是否所有属性赋值成功
|
||||
|
||||
foreach (var kvp in properties)
|
||||
{
|
||||
var propName = kvp.Key;
|
||||
var propValue = kvp.Value;
|
||||
|
||||
var propInfo = objType.GetProperty(propName);
|
||||
|
||||
if (propInfo == null)
|
||||
{
|
||||
// 属性不存在,打印警告并标记失败
|
||||
Console.WriteLine($"Warning: 属性 '{propName}' 不存在于类型 '{objType.Name}' 中,跳过赋值。");
|
||||
allSuccessful = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
// 检查属性类型是否与要赋的值兼容
|
||||
var targetType = propInfo.PropertyType;
|
||||
if (!IsCompatibleType(targetType, propValue))
|
||||
{
|
||||
// 如果类型不兼容,打印错误并标记失败
|
||||
Console.WriteLine($"Error: 无法将类型 '{propValue?.GetType().Name}' 赋值给属性 '{propName}' (Type: {targetType.Name}),跳过赋值。");
|
||||
allSuccessful = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
// 如果值是一个嵌套对象,递归赋值
|
||||
if (propValue is Dictionary<string, object> nestedProperties)
|
||||
{
|
||||
var nestedObj = Activator.CreateInstance(propInfo.PropertyType);
|
||||
if (nestedObj != null && SetPropertyValuesWithValidation(nestedObj, nestedProperties))
|
||||
{
|
||||
propInfo.SetValue(obj, nestedObj);
|
||||
}
|
||||
else
|
||||
{
|
||||
allSuccessful = false; // 嵌套赋值失败
|
||||
}
|
||||
}
|
||||
else if (propValue is IList<Dictionary<string, object>> list) // 处理列表
|
||||
{
|
||||
// 获取目标类型的数组元素类型
|
||||
var elementType = propInfo.PropertyType.GetElementType();
|
||||
if (elementType != null)
|
||||
{
|
||||
var array = Array.CreateInstance(elementType, list.Count);
|
||||
|
||||
for (int i = 0; i < list.Count; i++)
|
||||
{
|
||||
var item = Activator.CreateInstance(elementType);
|
||||
if (item != null && SetPropertyValuesWithValidation(item, list[i]))
|
||||
{
|
||||
array.SetValue(item, i);
|
||||
}
|
||||
else
|
||||
{
|
||||
allSuccessful = false; // 赋值失败
|
||||
}
|
||||
}
|
||||
propInfo.SetValue(obj, array);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// 直接赋值
|
||||
propInfo.SetValue(obj, propValue);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Console.WriteLine($"Error: 为属性 '{propName}' 赋值时发生异常:{ex.Message}");
|
||||
allSuccessful = false;
|
||||
}
|
||||
}
|
||||
|
||||
return allSuccessful;
|
||||
}
|
||||
|
||||
// 检查类型兼容性的方法(支持嵌套类型)
|
||||
static bool IsCompatibleType(Type targetType, object value)
|
||||
{
|
||||
if (value == null)
|
||||
{
|
||||
// 如果值为null,且目标类型是引用类型或者可空类型,则兼容
|
||||
return !targetType.IsValueType || Nullable.GetUnderlyingType(targetType) != null;
|
||||
}
|
||||
|
||||
// 检查值的类型是否与目标类型相同,或是否可以转换为目标类型
|
||||
if (targetType.IsAssignableFrom(value.GetType()))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
// 处理数组类型
|
||||
if (targetType.IsArray)
|
||||
{
|
||||
// 检查数组的元素类型与值的类型兼容
|
||||
var elementType = targetType.GetElementType();
|
||||
return value is IList<Dictionary<string, object>>; // 假设值是一个列表,具体处理逻辑在赋值时
|
||||
}
|
||||
|
||||
// 处理嵌套类型的情况
|
||||
if (value is Dictionary<string, object> && targetType.IsClass && !targetType.IsPrimitive)
|
||||
{
|
||||
// 如果目标类型是一个复杂对象,并且值是一个字典,可能是嵌套对象
|
||||
return true; // 假设可以递归处理嵌套对象
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
}
|
||||
@@ -22,7 +22,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
|
||||
public static T Evaluate(string expression, T inputValue)
|
||||
{
|
||||
|
||||
|
||||
// 替换占位符@为输入值
|
||||
expression = expression.Replace("@", inputValue.ToString());
|
||||
try
|
||||
@@ -51,7 +51,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
/// <exception cref="NotSupportedException"></exception>
|
||||
public static object Evaluate(string expression, object targetObJ, out bool isChange)
|
||||
{
|
||||
if(expression is null || targetObJ is null)
|
||||
if (expression is null || targetObJ is null)
|
||||
{
|
||||
throw new Exception("表达式条件expression is null、 targetObJ is null");
|
||||
}
|
||||
@@ -88,13 +88,13 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
throw new NotSupportedException($"Operation {operation} is not supported.");
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
private static readonly char[] separator = new char[] { '(', ')' };
|
||||
private static readonly char[] separator = new char[] { '(', ')' };
|
||||
private static readonly char[] separatorArray = new char[] { ',' };
|
||||
|
||||
/// <summary>
|
||||
@@ -128,6 +128,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
return method.Invoke(target, parameterValues);
|
||||
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 获取值
|
||||
/// </summary>
|
||||
@@ -156,56 +157,53 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
}
|
||||
|
||||
var targetType = target?.GetType(); // 目标对象的类型
|
||||
if(targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
#region 处理键值对
|
||||
if (targetType.IsGenericType && targetType.GetGenericTypeDefinition() == typeof(Dictionary<,>))
|
||||
{
|
||||
|
||||
var typetmp = target.GetType().FullName;
|
||||
// 目标是键值对
|
||||
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
|
||||
var method = targetType.GetMethod("get_Item", BindingFlags.Public | BindingFlags.Instance);
|
||||
if(method != null)
|
||||
if (method != null)
|
||||
{
|
||||
var result = method.Invoke(target, new object[] { indexStr });
|
||||
if(result != null)
|
||||
var result = method.Invoke(target, new object[] { indexStr });
|
||||
if (result != null)
|
||||
{
|
||||
return result;
|
||||
target = result;
|
||||
}
|
||||
}
|
||||
|
||||
//var dict = target as Dictionary<string, string>;
|
||||
////var dict = (Dictionary<dynamic, dynamic>)target;
|
||||
//var temp = dict[indexStr];
|
||||
////if (target is Dictionary<object, object> dict)
|
||||
////{
|
||||
//// var temp = dict[indexStr];
|
||||
////}
|
||||
//var TMP2= target.GetType().GetEnumValues();
|
||||
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
else
|
||||
{
|
||||
|
||||
#region 表达式处理集合对象
|
||||
// 获取数组或集合对象
|
||||
var arrayProperty = target?.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty is null)
|
||||
|
||||
// 如果arrayName为空,说明target可能是数组,而不需要再获取属性了
|
||||
if (!string.IsNullOrEmpty(arrayName))
|
||||
{
|
||||
var arrayField = target?.GetType().GetField(arrayName);
|
||||
if (arrayField is null)
|
||||
var arrayProperty = target?.GetType().GetProperty(arrayName);
|
||||
if (arrayProperty is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {arrayName} not found on target.");
|
||||
var arrayField = target?.GetType().GetField(arrayName);
|
||||
if (arrayField is null)
|
||||
{
|
||||
throw new ArgumentException($"Member {arrayName} not found on target.");
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayField.GetValue(target);
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
target = arrayProperty.GetValue(target);
|
||||
}
|
||||
|
||||
|
||||
|
||||
// 提取数组索引
|
||||
var indexStr = member.Substring(arrayIndexStart + 1, arrayIndexEnd - arrayIndexStart - 1);
|
||||
if (!int.TryParse(indexStr, out int index))
|
||||
@@ -236,7 +234,6 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
#endregion
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -288,7 +285,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
|
||||
// 检查是否包含数组索引
|
||||
var arrayIndexStart = member.IndexOf('[');
|
||||
if (arrayIndexStart != -1)
|
||||
if (arrayIndexStart != -1)
|
||||
{
|
||||
// 解析数组名和索引
|
||||
var arrayName = member.Substring(0, arrayIndexStart);
|
||||
@@ -394,6 +391,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
|
||||
return target;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 计算数学简单表达式
|
||||
/// </summary>
|
||||
@@ -408,7 +406,7 @@ namespace Serein.Library.Utils.SereinExpression
|
||||
private static T ComputedNumber<T>(object value, string expression) where T : struct, IComparable<T>
|
||||
{
|
||||
T result = value.ToConvert<T>();
|
||||
return SerinArithmeticExpressionEvaluator<T>.Evaluate(expression, result);
|
||||
return SerinArithmeticExpressionEvaluator<T>.Evaluate(expression, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user