You can compile and run code represented by expression trees. This enables dynamic modification of executable code, the execution of LINQ queries in various databases, and the creation of dynamic queries. For more information about expression trees in LINQ, see How to use expression trees to build dynamic queries (C#).
The Dynamic LINQ library exposes a set of extension methods on IQueryable corresponding to the standard LINQ methods at Queryable, and which accept strings in a special syntax instead of expression trees.
Expression Class (System.Linq.Expressions)Provides the base class from which the classes that represent expression tree nodes are derived. It also contains static (Shared in Visual Basic) factory methods to create the various node types. This is an abstract class.
Would the dynamic linq library help here? In particular, I'm thinking as a Where
clause. If necessary, put it inside a list/array just to call .Where(string)
on it! i.e.
var people = new List<Person> { person };
int match = people.Where(filter).Any();
If not, writing a parser (using Expression
under the hood) isn't hugely taxing - I wrote one similar (although I don't think I have the source) in my train commute just before xmas...
Another such library is Flee
I did a quick comparison of Dynamic Linq Library and Flee and Flee was 10 times faster for the expression "(Name == \"Johan\" AND Salary > 500) OR (Name != \"Johan\" AND Salary > 300)"
This how you can write your code using 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();
}
void Main()
{
var testdata = new List<Ownr> {
//new Ownr{Name = "abc", Qty = 20}, // uncomment this to see it getting filtered out
new Ownr{Name = "abc", Qty = 2},
new Ownr{Name = "abcd", Qty = 11},
new Ownr{Name = "xyz", Qty = 40},
new Ownr{Name = "ok", Qty = 5},
};
Expression<Func<Ownr, bool>> func = Extentions.strToFunc<Ownr>("Qty", "<=", "10");
func = Extentions.strToFunc<Ownr>("Name", "==", "abc", func);
var result = testdata.Where(func.ExpressionToFunc()).ToList();
result.Dump();
}
public class Ownr
{
public string Name { get; set; }
public int Qty { get; set; }
}
public static class Extentions
{
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 type = typeof(T);
var prop = type.GetProperty(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 (Exception ex)
{
ex.Dump();
}
return func;
}
private static Expression ToExprConstant(PropertyInfo prop, string value)
{
object val = null;
try
{
switch (prop.Name)
{
case "System.Guid":
val = Guid.NewGuid();
break;
default:
{
val = Convert.ChangeType(value, prop.PropertyType);
break;
}
}
}
catch (Exception ex)
{
ex.Dump();
}
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)
{
var res = expr.Compile();
return res;
}
}
LinqPad has the Dump()
method
You might take a look at the DLR. It allows you to evaluate and execute scripts inside .NET 2.0 application. Here's a sample with 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; }
}
Of course this technique is based on runtime evaluation and code cannot be verified at compile time.
Here is an example of a Scala DSL based parser combinator for parsing and evaluating of arithmetic expressions.
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)
}
}
The equivalent expression tree or parse tree of the provided arithmetic expression would be of the Parser[List[String]] type.
More details are at the following link:
http://nicolaecaralicea.blogspot.ca/2013/04/scala-dsl-for-parsing-and-evaluating-of.html
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With