I'm attempting to build out some additional feature for a rules engine that I'm working on using Expression Trees which is based on this post - How to implement a rule engine?
One of the rules I have is to add minutes to a property if that properties type is found to be a DateTime. So for example I could have "DOB + 60" or "DOB - 180".
My current code is working for this:
var expression = new string[] {"DOB", "+", "60"};
var property = Expression.Property(param, expression[0]);
var method = typeof(T).GetProperty(expression[0]).PropertyType.GetMethod("AddMinutes");
/* If we have a AddMinutes method then assume type is of DateTime */
if (method != null)
{
var tParam = method.GetParameters()[0].ParameterType;
/* Calculate if we're adding or subtracting minutes */
var convertedNo = expression[1] == "-" ? int.Parse(expression[2]) * -1 : int.Parse(expression[2]);
var number = Expression.Constant(Convert.ChangeType(convertedNo, tParam));
var exp = Expression.Call(property, method, number);
/* Allows for left property < DOB.AddMinutes(60) */
return Expression.MakeBinary(ExpressionType.LessThan, left, exp);
}
The trouble I'm having is that the DOB property may be set to DateTime.Min so that when my expression comes to evaluate "DOB - 180" in that case an Out of Bounds exception is thrown as it's attempting to subtract 180 mins from DateTime.Min.
I attempted to get round this issue by using an Expression.IfThenElse so that if the DateTime property is equal to DateTime.Min, don't do the calculation and just evaluate on DateTime.Min.
My updated code looks like this:
var expression = new string[] {"DOB", "-", "180"};
var property = Expression.Property(param, expression[0]);
var method = typeof(T).GetProperty(expression[0]).PropertyType.GetMethod("AddMinutes");
/* If we have a AddMinutes method then assume type is of DateTime */
if (method != null)
{
var tParam = method.GetParameters()[0].ParameterType;
/* Calculate if we're adding or subtracting minutes */
var convertedNo = expression[1] == "-" ? int.Parse(expression[2]) * -1 : int.Parse(expression[2]);
var number = Expression.Constant(Convert.ChangeType(convertedNo, tParam));
var exp = Expression.Call(property, method, number);
var minDate = Expression.Constant(Convert.ChangeType(DateTime.MinValue, typeof(DateTime)));
var minDateCheck = Expression.MakeBinary(ExpressionType.Equal, property, minDate);
var minDateExp = Expression.IfThenElse(minDateCheck, minDate, exp);
/* To allow for left property < DOB == DateTime.Min ? DateTime.Min : DOB.AddMinutes(60) */
return Expression.MakeBinary(ExpressionType.LessThan, left, minDateExp); // Exception thrown here
}
Unfortunately this throws the exception - Cannot compare DateTime with return type of void. I've tried several variations but always get the same exception.
Would anyone be able to show me how I could construct an expression that would be able to do this logic?
Expression.IfThenElse is equivalent of the C# - well, if statement, while what you need is the expression equivalent of the C# ?: (a.k.a conditional) operator, which is ConditionalExpression and is produced by Expression.Condition method.
Shortly, replace
var minDateExp = Expression.IfThenElse(minDateCheck, minDate, exp);
with
var minDateExp = Expression.Condition(minDateCheck, minDate, exp);
and you are done.
Related
I am looking for a way to get a boolean value from a string.
I have a string, stored in a database, that is a condition to be tested with.
suppose the string = "[number] < 10000"
In my code I then plan to replace [number] with a actual number so the string would become "100 < 10000".
Is there a simple way to evaluate this string to a boolean.
In this case it would be False.
Another example: "[number] > 100 && [number] < 1000"
If number would be 200, the evaluation would be True.
What if I add code to this condition, like "if ([number] > 100 && [number] < 1000) { return true;} else {return false;}"
If there a simple way to get the return value from this string with code?
You can make use of DynamicExpression class, it is available in this nuget package. It can evaluate expressions on the fly.
Below is an example,
var expression = System.Linq.Dynamic.DynamicExpression.ParseLambda(
new System.Linq.Expressions.ParameterExpression[] { },
typeof(bool),
"100 > 1000",
new object[] { });
var compiledDelegate = expression.Compile();
var result = compiledDelegate.DynamicInvoke(null);
The result will be false as the provided expression "100 > 1000" is false.
If you need more control, you have to create expressions depending upon dynamic strings representing logical operations. For example, I have taken your first case of "[number]<1000" You can try below:
static void Main(string[] args)
{
string code = "[number] < 10000".Replace("[number]", "100");
string[] operands = code.Split('<');
var comparison = Expression.LessThan(
Expression.Constant(operands[0]),
Expression.Constant(operands[1]));
var lambda = Expression.Lambda<Func<bool>>(comparison).Compile();
bool result = lambda();
}
Or Alternatively you can use existing libraries or nuget packages for doing same thing in a more simpler way (under hood they do same though):
string code = "[number] < 10000".Replace("[number]", "100");
func = ExpressionParser.Compile<Func<bool>>(code);
bool result = func()
It makes use of Lambda parser as shown here
i have a project to create a boolean table (incidence matrix) for an information retrieval system, so i created 3 files and i took the words from each file and removed the stopping words from them, and added them to a listView using c# language, and all i need is to be able to take the query from a textBox and convert it into a suitable boolean expression so that it will return the names of documents that the satisfies the boolean expression.
eg. "Ahmed and love and not ali"
This should give me Ahmed && love && ! Ali which will be true for doc1.
Using String.Replace
string src = "Ahmed and love and not ali";
string res = src.Replace("and", "&&")
.Replace("not", "!");
string[] parts = res.Split(' '); // { Ahmed , && , love , && , ! , ali }
//Do Shunting-Yard algorithm to convert infix expression to postfix
ToPostfix(parts); // Ahmed love && ali ! &&
bool result = PerformRPN(parts); //Calculate postfix, you should read the Reverse Polish Notation article
Link to the wikis (updated the shunting-yard reference to a closer version to the OP problem):
Shunting-Yard algorithm
Reverse Polish Notation
There are a few key concept in the algorithm that i'll explain further
IsOperator
bool IsOperator(string part) {return part=="&&" || part=="||" || part == "!" || ...}
IsOperand - is not Operator
If you need anymore help, let me know
Extra: (as Jared suggest)
You should initialize your table value in a dictionary
Dictionary<string, bool> doc1 = new Dictionary<string, bool>();
doc1.Add("Ahmed", doc1BooleanArray[0]);
doc1.Add("love", doc1BooleanArray[1]);
...
So when you need to get your operand value to calculate expression just use:
//Get operand_part = "Ahmed"
bool val = doc1[operand_part]
The simplest thing I could think of was to use a DataTable.
This should handle all "happy paths" correctly. Like it says in the comments you may need to do additional validation to explicitly error out on what you consider invalid input.
class Program
{
static void Main(string[] args)
{
// Unit test the EvaluateBinaryExpression method.
Console.Out.WriteLine(EvaluateBinaryExpression("true")); // true
Console.Out.WriteLine(EvaluateBinaryExpression("false")); // false
Console.Out.WriteLine(EvaluateBinaryExpression("true or false")); // true
Console.Out.WriteLine(EvaluateBinaryExpression("false or false")); // false
Console.Out.WriteLine(EvaluateBinaryExpression("true and false")); // false
Console.Out.WriteLine(EvaluateBinaryExpression("true and true")); // true
Console.Out.WriteLine(EvaluateBinaryExpression("true and not false")); // false
//
// This should give me Ahmed && love && ! Ali which will be true for doc1.
// TODO: get these values out of the table.
var doc1Results = new Dictionary<string, bool>()
{
{ "Ahmed", true },
{ "love", true },
{ "ali", false }
};
// Then just call it like this
Console.Out.WriteLine(EvaluateResults("Ahmed and love and not ali", doc1Results)); // true
}
public static bool EvaluateResults(string expression, IDictionary<string, bool> results)
{
// TODO validate input expression and results
// Make sure the expression is padded with whitespace for replacing.
var replaced = " " + expression + " ";
foreach (var kvp in results)
{
var search = " " + kvp.Key + " ";
var replacement = " " + kvp.Value.ToString() + " ";
// TODO make sure casing, etc. doesn't matter.
replaced = replaced.Replace(search, replacement);
}
return EvaluateBinaryExpression(replaced);
}
public static bool EvaluateBinaryExpression(string expression)
{
// TODO what if this throws an error?
var dt = new System.Data.DataTable();
dt.Columns.Add("Test", typeof(object));
dt.Rows.Add(new object[] { null });
dt.Columns[0].Expression = expression;
var value = dt.Rows[0][0] as bool?;
return value.Value;
}
}
When I use Expression.ToString() to convert an Expression Tree into human readable form, the result is something like this:
x => ((x.ID > 2) OrElse (x.ID != 6))
x => ((x.ID > 2) AndAlso (x.ID != 6))
Ideally, I would want the output to show the operators instead of "OrElse" and "AndAlso":
x => ((x.ID > 2) || (x.ID != 6))
x => ((x.ID > 2) && (x.ID != 6))
As a workaround, I could use the string.Replace() method..
.Replace("AndAlso", "&&")
.Replace("OrElse", "||")
but that has obvious weaknesses and seems awkward.
Also I do not want to create a large 'Replace'-section or huge regex-tree simply to get the formatting right.
Is there a simple way to get a code-like human-readable form of expression trees?
Unfortunately the simplest way to do this correctly would be to write your own ExpressionVisitor class which produces the C#-formatted output code.
The simplest way to do this is to use the ExpressionStringBuilder from the reference sources as a starting point and to tweak it until you are happy with the output result.
When I'm interested in the semantics of the code represented by the expression, rather than the exact syntax tree, I've found it very useful to compile it to an Assembly and view that in ILSpy. Convenience method:
// Code is probably adapted from some other answer, don't remember
public static void CompileToAssemblyFile(
this LambdaExpression expression,
string outputFilePath = null,
string assemblyAndModuleName = null,
string typeName = "TheType",
string methodName = "TheMethod",
// Adjust this
string ilSpyPath = #"C:\path\to\ILSpy.exe")
{
assemblyAndModuleName = assemblyAndModuleName ?? nameof(CompileToAssemblyFile);
outputFilePath = outputFilePath ??
Path.Combine(
Path.GetTempPath(),
$"{assemblyAndModuleName}_{DateTime.Now:yyyy-MM-dd_HH_mm_ss}_{Guid.NewGuid()}.dll");
var domain = AppDomain.CurrentDomain;
var asmName = new AssemblyName {Name = assemblyAndModuleName};
var asmBuilder = domain.DefineDynamicAssembly(
asmName,
AssemblyBuilderAccess.RunAndSave,
Path.GetDirectoryName(outputFilePath));
string outputFileName = Path.GetFileName(outputFilePath);
var module = asmBuilder.DefineDynamicModule(
assemblyAndModuleName,
outputFileName,
true);
var typeBuilder = module.DefineType(typeName, TypeAttributes.Public);
var methodBuilder = typeBuilder.DefineMethod(
methodName,
MethodAttributes.Public | MethodAttributes.Static,
expression.ReturnType,
expression.Parameters.Select(p => p.Type).ToArray());
var pdbGenerator = DebugInfoGenerator.CreatePdbGenerator();
expression.CompileToMethod(methodBuilder, pdbGenerator);
typeBuilder.CreateType();
asmBuilder.Save(outputFileName);
Process.Start(ilSpyPath, outputFilePath);
}
(This is not very faithful to the syntax tree because it goes through both the Expression -> IL translation done by the LambdaCompiler, and the IL -> C# decompilation by ILSpy. OTOH, it can improve readability by converting some gotos into loops, and by producing actual C#.)
This will fail if the Expression contains "non-trivial constants" (live objects); but for that one could write a visitor that replaces constants by new variables and then lambda-abstracts these variables at the toplevel.
I have a scenario in which I am saving my "if" conditions in database as a string. For example:
String condition = "(([age] >= 28) && ([nationality] == 'US'))";
OR
String condition = "([age] >= 28)";
Now, I want to evaluate that the user has input the condition syntactically correct. These are example of incorrect syntax:
String condition = "(([age] >= 28) && ([nationality] == 'US')"; //Missed ')' bracket
String condition = "[age] >= 28)"; //Missed Opening bracket '('
Like we have in Evaluate Query Expression. Might be Expression tress can be helpful. But how? Need help in this regard.
Take a look at NCalc. It's a framework for evaluating mathematical expressions.
When the expression has a syntax error, the evaluation will throw an EvaluationException.
try
{
new Expression("(3 + 2").Evaluate();
}
catch(EvaluationException e)
{
Console.WriteLine("Error catched: " + e.Message);
}
Though, you can also detect syntax errors before the evaluation by using the HasErrors() method.
Expression e = new Expression("a + b * (");
if(e.HasErrors())
{
Console.WriteLine(e.Error);
}
Visual studio doesn't really know what the strings represent so to my knowledge there is no parsing done within the strings themselves.
Typically when programming with C# and using sql, you'd try to do as much of the calculations as possible in C# itself (if it's feasible select the whole table then deal with the result using C#).
If the database is really slow which is quite often the case, it may be useful writing a SQL Builder class to deal with the hardcoded strings.
If you use neither of these methods, unfortunately the best you can really hope for is runtime exceptions (which isn't optimal for obvious reasons).
EDIT:
It seems a SelectQueryBuilder library already exists for the second scenario I suggested.
I found this solution
evaluate an arithmetic expression stored in a string (C#)
SOLUTION:
string conditiontext = "(([age] >= 28) && ([nationality] == \"US\"))";
conditiontext = conditiontext.Replace("[age]", 32)
.Replace("[nationality]","US");
/*VsaEngine*/
var engine = Microsoft.JScript.Vsa.VsaEngine.CreateEngine();
/** Result will be either true or false based on evaluation string*/
var result = Microsoft.JScript.Eval.JScriptEvaluate(conditiontext, engine);
[Note: This interface is deprecated. But it evaluates any arithmetic expressions and c# expressions]
You could use System.Data and its DataTable.Compute() method.
Here is the code:
public bool CheckCondition()
{
// parameters
(string name, object value)[] variables =new (string name, object value)[1];
variables[0].name = "age";
variables[0].value = 28;
variables[1].name = "nationality";
variables[1].value = "US";
string conditions = "(([age] >= 28) && ([nationality] == 'US'))";
conditions.Replace("[", "").Replace("]", "").Replace("&&", "AND").Replace("||", "OR");
using DataTable table = new DataTable();
foreach (var (name, value) in variables)
table.Columns.Add(name, value is null ? typeof(object) : value.GetType());
table.Rows.Add();
foreach (var (name, value) in variables)
table.Rows[0][name] = value;
table.Columns.Add("_Result", typeof(double)).Expression = conditions
?? throw new ArgumentNullException(nameof(conditions));
return (bool)(Convert.ChangeType(table.Compute($"Min(_Result)", null), typeof(bool)));
}
Basically I have some code where when it happens, I need to set some object equal to some expression. All of this "what to do" jazz is stored as a string. So I parse it, and use reflection to find the object I am doing it to. Now I need to find out how to store the value to this object. The problem is the value could be "1", "1*(5/2)", or "some string value". It would be really cool if I could have expressions like "this.SomeProperty" or "(x > 3 ? 4 : 5)".
Also, the object it is storing to, could be a string, int, double, or float at the minimum.
The VS2008 samples included a nifty ExpressionParser which could be used as a generic expression parser (VS2008 Samples). With a few small updates, and a custom factory class, we can turn it into something a bit more expressive:
string expression = "(1 + 2)";
var func = FunctionFactory.Create<int>(expression);
Or:
expression = "(a * b)";
var func2 = FunctionFactory.Create<int, int, int>(expression, new[] { "a", "b" });
The return types of these Create methods are Func<> instances, which means we get nice strongly type delegates:
int result = func2(45, 100); // result = 450;
I've push the code to a gist
Update: I've recently blogged about this too.
Update 2, another example:
var person = new Person { Age = 5 };
string expression = "(Age == 5)";
var func3 = FunctionFactory.Create<Person, bool>(expression);
bool isFive = func3(person); // Should be true.
Have you seen http://ncalc.codeplex.com ?
It's extensible, fast (e.g. has its own cache) enables you to provide custom functions and varaibles at run time by handling EvaluateFunction/EvaluateParameter events. Example expressions it can parse:
Expression e = new Expression("Round(Pow(Pi, 2) + Pow([Pi2], 2) + X, 2)");
e.Parameters["Pi2"] = new Expression("Pi * Pi");
e.Parameters["X"] = 10;
e.EvaluateParameter += delegate(string name, ParameterArgs args)
{
if (name == "Pi")
args.Result = 3.14;
};
Debug.Assert(117.07 == e.Evaluate());
It also handles unicode & many data type natively. It comes with an antler file if you want to change the grammer. There is also a fork which supports MEF to load new functions.