如何将string转换为其等价的LINQexpression式树?

这是原始问题的简化版本。

我有一个叫Person的类:

public class Person { public string Name { get; set; } public int Age { get; set; } public int Weight { get; set; } public DateTime FavouriteDay { get; set; } } 

…让我们说一个实例:

 var bob = new Person { Name = "Bob", Age = 30, Weight = 213, FavouriteDay = '1/1/2000' } 

我想写在我最喜欢的文本编辑器中的string以下内容….

 (Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3 

我想采取这个string和我的对象实例,并评估一个TRUE或FALSE – 即在对象实例上评估一个Func <Person,bool>。

这是我目前的想法:

  1. 在ANTLR中实现一个基本的语法来支持基本的比较和逻辑运算符。 我想在这里复制Visual Basic的优先级和一些function集: http : //msdn.microsoft.com/en-us/library/fw84t893(VS.80).aspx
  2. 让ANTLR从提供的string中创build一个合适的AST。
  3. 走AST并使用Predicate Builder框架dynamic创buildFunc <Person,bool>
  4. 根据需要针对Person实例评估谓词

我的问题是我完全过度烧烤? 任何替代品?


编辑:select的解决scheme

我决定使用dynamicLinq库,特别是LINQSamples中提供的dynamic查询类。

代码如下:

 using System; using System.Linq.Expressions; using System.Linq.Dynamic; namespace ExpressionParser { class Program { public class Person { public string Name { get; set; } public int Age { get; set; } public int Weight { get; set; } public DateTime FavouriteDay { get; set; } } static void Main() { const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3"; var p = Expression.Parameter(typeof(Person), "Person"); var e = DynamicExpression.ParseLambda(new[] { p }, null, exp); var bob = new Person { Name = "Bob", Age = 30, Weight = 213, FavouriteDay = new DateTime(2000,1,1) }; var result = e.Compile().DynamicInvoke(bob); Console.WriteLine(result); Console.ReadKey(); } } } 

结果是System.Booleantypes的,在这个例子中是TRUE。

非常感谢Marc Gravell。

dynamicLINQ库会在这里帮助吗? 特别是,我正在考虑作为Where子句。 如果有必要的话,把它放在一个列表/数组中,只需要调用.Where(string)就可以了! 即

 var people = new List<Person> { person }; int match = people.Where(filter).Any(); 

如果不是,写一个parsing器(使用Expression )并不是非常重要的 – 我在我的火车通勤之前写了一个类似的(虽然我不认为我有源码)…

另一个这样的图书馆是Flee

我做了一个dynamicLinq库的快速比较, Flee和Flee对于expression式"(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"

这就是你如何使用Flee编写你的代码。

 static void Main(string[] args) { var context = new ExpressionContext(); const string exp = @"(Person.Age > 3 AND Person.Weight > 50) OR Person.Age < 3"; context.Variables.DefineVariable("Person", typeof(Person)); var e = context.CompileDynamic(exp); var bob = new Person { Name = "Bob", Age = 30, Weight = 213, FavouriteDay = new DateTime(2000, 1, 1) }; context.Variables["Person"] = bob; var result = e.Evaluate(); Console.WriteLine(result); Console.ReadKey(); } 
  public static Expression<Func<T, bool>> strToFunc<T>(string propName, string opr, string value, Expression<Func<T, bool>> expr = null) { Expression<Func<T, bool>> func = null; try { var prop = GetProperty<T>(propName); ParameterExpression tpe = Expression.Parameter(typeof(T)); Expression left = Expression.Property(tpe, prop); Expression right = Expression.Convert(ToExprConstant(prop, value), prop.PropertyType); Expression<Func<T, bool>> innerExpr = Expression.Lambda<Func<T, bool>>(ApplyFilter(opr, left, right), tpe); if (expr != null) innerExpr = innerExpr.And(expr); func = innerExpr; } catch { } return func; } private static Expression ToExprConstant(PropertyInfo prop, string value) { object val = null; switch (prop.PropertyTypeName()) { case "System.Guid": val = value.ToGuid(); break; default: val = Convert.ChangeType(value, Type.GetType(prop.PropertyTypeName())); break; } return Expression.Constant(val); } private static BinaryExpression ApplyFilter(string opr, Expression left, Expression right) { BinaryExpression InnerLambda = null; switch (opr) { case "==": case "=": InnerLambda = Expression.Equal(left, right); break; case "<": InnerLambda = Expression.LessThan(left, right); break; case ">": InnerLambda = Expression.GreaterThan(left, right); break; case ">=": InnerLambda = Expression.GreaterThanOrEqual(left, right); break; case "<=": InnerLambda = Expression.LessThanOrEqual(left, right); break; case "!=": InnerLambda = Expression.NotEqual(left, right); break; case "&&": InnerLambda = Expression.And(left, right); break; case "||": InnerLambda = Expression.Or(left, right); break; } return InnerLambda; } public static Expression<Func<T, TResult>> And<T, TResult>(this Expression<Func<T, TResult>> expr1, Expression<Func<T, TResult>> expr2) { var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>()); return Expression.Lambda<Func<T, TResult>>(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters); } public static Func<T, TResult> ExpressionToFunc<T, TResult>(this Expression<Func<T, TResult>> expr) { return expr.Compile(); } 

例如:

 var testdata = EntityRepository.Get<Ownr>().ToList(); System.Linq.Expressions.Expression<Func<Ownr, bool>> func = Extentions.strToFunc<Ownr>("OWNR_START_DT", "<=", "2013-10-30"); func = Extentions.strToFunc<Ownr>("OWNR_NM", "==", "OwnerSystem1", func); var result = testdata.Where(func.ExpressionToFunc()).ToList(); 

你可以看看DLR 。 它允许您在.NET 2.0应用程序中评估和执行脚本。 以下是IronRuby的示例:

 using System; using IronRuby; using IronRuby.Runtime; using Microsoft.Scripting.Hosting; class App { static void Main() { var setup = new ScriptRuntimeSetup(); setup.LanguageSetups.Add( new LanguageSetup( typeof(RubyContext).AssemblyQualifiedName, "IronRuby", new[] { "IronRuby" }, new[] { ".rb" } ) ); var runtime = new ScriptRuntime(setup); var engine = runtime.GetEngine("IronRuby"); var ec = Ruby.GetExecutionContext(runtime); ec.DefineGlobalVariable("bob", new Person { Name = "Bob", Age = 30, Weight = 213, FavouriteDay = "1/1/2000" }); var eval = engine.Execute<bool>( "return ($bob.Age > 3 && $bob.Weight > 50) || $bob.Age < 3" ); Console.WriteLine(eval); } } public class Person { public string Name { get; set; } public int Age { get; set; } public int Weight { get; set; } public string FavouriteDay { get; set; } } 

当然,这种技术是基于运行时评估的,编译时不能validation代码。

下面是一个基于Scala DSLparsing器组合器的例子,用于parsing和评估算术expression式。

 import scala.util.parsing.combinator._ /** * @author Nicolae Caralicea * @version 1.0, 04/01/2013 */ class Arithm extends JavaTokenParsers { def expr: Parser[List[String]] = term ~ rep(addTerm | minusTerm) ^^ { case termValue ~ repValue => termValue ::: repValue.flatten } def addTerm: Parser[List[String]] = "+" ~ term ^^ { case "+" ~ termValue => termValue ::: List("+") } def minusTerm: Parser[List[String]] = "-" ~ term ^^ { case "-" ~ termValue => termValue ::: List("-") } def term: Parser[List[String]] = factor ~ rep(multiplyFactor | divideFactor) ^^ { case factorValue1 ~ repfactor => factorValue1 ::: repfactor.flatten } def multiplyFactor: Parser[List[String]] = "*" ~ factor ^^ { case "*" ~ factorValue => factorValue ::: List("*") } def divideFactor: Parser[List[String]] = "/" ~ factor ^^ { case "/" ~ factorValue => factorValue ::: List("/") } def factor: Parser[List[String]] = floatingPointConstant | parantExpr def floatingPointConstant: Parser[List[String]] = floatingPointNumber ^^ { case value => List[String](value) } def parantExpr: Parser[List[String]] = "(" ~ expr ~ ")" ^^ { case "(" ~ exprValue ~ ")" => exprValue } def evaluateExpr(expression: String): Double = { val parseRes = parseAll(expr, expression) if (parseRes.successful) evaluatePostfix(parseRes.get) else throw new RuntimeException(parseRes.toString()) } private def evaluatePostfix(postfixExpressionList: List[String]): Double = { import scala.collection.immutable.Stack def multiply(a: Double, b: Double) = a * b def divide(a: Double, b: Double) = a / b def add(a: Double, b: Double) = a + b def subtract(a: Double, b: Double) = a - b def executeOpOnStack(stack: Stack[Any], operation: (Double, Double) => Double): (Stack[Any], Double) = { val el1 = stack.top val updatedStack1 = stack.pop val el2 = updatedStack1.top val updatedStack2 = updatedStack1.pop val value = operation(el2.toString.toDouble, el1.toString.toDouble) (updatedStack2.push(operation(el2.toString.toDouble, el1.toString.toDouble)), value) } val initial: (Stack[Any], Double) = (Stack(), null.asInstanceOf[Double]) val res = postfixExpressionList.foldLeft(initial)((computed, item) => item match { case "*" => executeOpOnStack(computed._1, multiply) case "/" => executeOpOnStack(computed._1, divide) case "+" => executeOpOnStack(computed._1, add) case "-" => executeOpOnStack(computed._1, subtract) case other => (computed._1.push(other), computed._2) }) res._2 } } object TestArithmDSL { def main(args: Array[String]): Unit = { val arithm = new Arithm val actual = arithm.evaluateExpr("(12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) * ( 5 + 12 ))") val expected: Double = (12 + 4 * 6) * ((2 + 3 * ( 4 + 2 ) ) * ( 5 + 12 )) assert(actual == expected) } } 

提供的算术expression式的等效expression式树或分析树将是Parser [List [String]]types。

更多细节在以下链接:

http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html

除了dynamicLinq库(它构build强typesexpression式并需要强typesvariables)之外,我还推荐使用更好的替代方法:linqparsing器,它是NReco Commons Library (开源)的一部分。 它将所有typesalignment,并在运行时执行所有调用,并像dynamic语言一样运行:

 var lambdaParser = new NReco.LambdaParser(); var varContext = new Dictionary<string,object>(); varContext["one"] = 1M; varContext["two"] = "2"; Console.WriteLine( lambdaParser.Eval("two>one && 0<one ? (1+8)/3+1*two : 0", varContext) ); // --> 5