C#实现类似PHP eval功能 校验对象与数据库存储属性比较表达式是否匹配
C# 实现动态校验属性比较表达式方案
核心思路
使用Roslyn C#脚本引擎实现动态表达式解析执行,支持任意合法的C#属性比较逻辑,同时通过表达式缓存优化重复调用的性能。
前置依赖
首先安装所需NuGet包:
Install-Package Microsoft.CodeAnalysis.CSharp.Scripting
完整实现代码
using System; using System.Collections.Concurrent; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; public class ExpressionEvaluator { // 缓存已编译的表达式委托,避免重复编译损耗性能 private static readonly ConcurrentDictionary<string, Func<object, bool>> _expressionCache = new(); public static bool CheckMatch(object payment, string stringToCheck) { // 拼接缓存key,避免不同逻辑的表达式冲突 var cacheKey = $"{payment.GetType().FullName}|{stringToCheck}"; var evalFunc = _expressionCache.GetOrAdd(cacheKey, key => { // 构造脚本执行的全局上下文 var scriptOptions = ScriptOptions.Default // 添加上下文需要的程序集引用,可根据实际需求扩展 .AddReferences(payment.GetType().Assembly) .AddImports("System"); // 构造可执行的表达式脚本,用dynamic适配不同类型的输入对象 var script = $"((dynamic)Payment).{stringToCheck}"; // 编译为委托 return CSharpScript.EvaluateAsync<Func<object, bool>>( @"return (Payment) => " + script + ";", scriptOptions ).Result; }); // 执行委托返回结果 return evalFunc(payment); } } // 示例业务类 public class PaymentInfo { public long UidAsLong { get; set; } public string Name { get; set; } }
使用示例
// 测试实例 var testPayment = new PaymentInfo { UidAsLong = 18, Name = "Visa" }; // 测试表达式1,输出True Console.WriteLine(ExpressionEvaluator.CheckMatch(testPayment, "UidAsLong == 18")); // 测试表达式2,输出True Console.WriteLine(ExpressionEvaluator.CheckMatch(testPayment, "Name.Equals('Visa')")); // 测试组合表达式,输出True Console.WriteLine(ExpressionEvaluator.CheckMatch(testPayment, "UidAsLong > 10 && Name.StartsWith('V')"));
注意事项
- 安全风险提示:该实现会执行传入的任意C#表达式,请确保数据库中存储的表达式全部来自可信来源,禁止直接执行用户未校验的输入内容,避免代码注入风险。
- 如果不需要支持任意动态类型,可将代码中的
dynamic替换为明确的业务类型(比如PaymentInfo),可以获得更好的执行性能和类型校验。 - 缓存策略可根据实际业务调整,比如添加过期时间清理不常用的表达式。
内容的提问来源于stack exchange,提问作者Rodrigo
相关产品推荐
相关产品推荐

