能否解析字符串为函数并引用运行时变量?ExpandoObject实践疑问
问题原因与解决方案
报错原因
你调用DynamicExpressionParser.ParseLambda时,解析器的上下文并没有关联到你的ExpandoObject——默认情况下,解析器不会自动识别你存入sellerEx的Seller属性,必须通过显式注册变量或传入上下文参数的方式,让解析器能找到公式中的标识符。
可行实现方案
以下基于System.Linq.Dynamic.Core库(当前主流的动态表达式解析库)提供两种可行方案,均支持多数据源(如Schedule、ProductList):
方案1:通过解析配置注册上下文变量
将所有数据源(Seller、Schedule等)和后续计算出的属性注册到解析器的变量列表中,让公式直接引用标识符:
using System.Dynamic; using System.Linq.Dynamic.Core; // 初始化基础数据 var seller = new Seller { MonthlyAmount = 1500 }; var schedule = new Schedule { DiscountRate = 0.02m }; // 示例其他数据源 // 创建动态对象存储数据源与计算结果 var dynamicContainer = new ExpandoObject() as IDictionary<string, object>; dynamicContainer["Seller"] = seller; dynamicContainer["Schedule"] = schedule; // 按依赖顺序排序公式(先算无依赖的Bonus,再算依赖它的AmountPlusBonus) var orderedFormulas = formulas.OrderBy(f => f.Value.Contains("Bonus") ? 1 : 0).ToList(); var parsingConfig = new ParsingConfig(); // 先注册所有基础数据源到解析变量 foreach (var kvp in dynamicContainer.Where(kvp => !(kvp.Value is LambdaExpression))) { parsingConfig.Variables[kvp.Key] = kvp.Value; } foreach (var formula in orderedFormulas) { // 解析公式为无参数Lambda var lambda = DynamicExpressionParser.ParseLambda(parsingConfig, Type.EmptyTypes, typeof(object), formula.Value); // 执行计算并存储结果 var result = lambda.DynamicInvoke(); dynamicContainer[formula.Key] = result; // 将新结果注册为变量,供后续公式引用 parsingConfig.Variables[formula.Key] = result; } // 测试访问结果 dynamic dynContainer = dynamicContainer; Console.WriteLine(dynContainer.Bonus); // 输出45 Console.WriteLine(dynContainer.AmountPlusBonus); // 输出1545 Console.WriteLine(dynContainer.Schedule.DiscountRate); // 输出0.02
方案2:将动态对象作为Lambda参数传入
把ExpandoObject作为Lambda的输入参数,公式中用默认参数名it访问上下文属性,更符合Lambda的参数传递逻辑:
using System.Dynamic; using System.Linq.Dynamic.Core; var seller = new Seller { MonthlyAmount = 1500 }; var dynamicContext = new ExpandoObject() as IDictionary<string, object>; dynamicContext["Seller"] = seller; dynamicContext["Schedule"] = new Schedule { DiscountRate = 0.02m }; // 处理公式依赖顺序 var orderedFormulas = formulas.OrderBy(f => f.Value.Contains("Bonus") ? 1 : 0).ToList(); foreach (var formula in orderedFormulas) { // 调整公式,用it.xxx访问上下文属性 var adjustedFormula = formula.Value .Replace("Seller", "it.Seller") .Replace("Bonus", "it.Bonus") .Replace("Schedule", "it.Schedule"); // 解析Lambda,参数类型为ExpandoObject var lambda = DynamicExpressionParser.ParseLambda(new[] { typeof(ExpandoObject) }, typeof(object), adjustedFormula); // 传入上下文执行计算 var result = lambda.DynamicInvoke(dynamicContext); dynamicContext[formula.Key] = result; } // 访问结果 dynamic dynContext = dynamicContext; Console.WriteLine(dynContext.Bonus);
多数据源支持说明
完全可行。只需将Schedule、ProductList等对象添加到ExpandoObject中,通过上述方案的任一方式,即可在公式中直接引用这些数据源的属性,例如公式可以写为:Seller.MonthlyAmount * (1 - Schedule.DiscountRate)。
注意事项
- 必须处理公式的依赖顺序:如果公式B依赖公式A的结果,必须先计算A再计算B;复杂依赖场景可通过拓扑排序确定执行顺序。
- 确保项目引用
System.Linq.Dynamic.CoreNuGet包,原System.Linq.Dynamic库已停止维护。
内容的提问来源于stack exchange,提问作者zameb
相关产品推荐
相关产品推荐

