I have the following class:
public class Test
{
public string Text { get; set; }
public int Number { get; set; }
}
And I'm creating and Expression tree of type Expression<Func<Test, bool>> on this class. When I do it like this:
Expression<Func<Test, bool>> predicate1 = x => x.Text.Length > 5 && x.Number > 0;
I get the following debug view:
.Lambda #Lambda1<System.Func`2[NHLinqTest.Test,System.Boolean]>(NHLinqTest.Test $x) {
($x.Text).Length > 5 && $x.Number > 0
}
note: there's a && for and-operation.
When I do it like this:
var y = Expression.Parameter(typeof(Test));
var predicate2 = Expression.And(
Expression.GreaterThan(
Expression.Property(Expression.Property(y, "Text"), "Length"),
Expression.Constant(5)),
Expression.GreaterThan(
Expression.Property(y, "Number"),
Expression.Constant(0)));
I get the following debug view:
($var1.Text).Length > 5 & $var1.Number > 0
Note: there's & for and-operation. Why do I get & in the second case? How to modify predicate2 to get && instead of &?
Thanks in advance!
Because it is & - i.e. bitwise / non-short-circuiting "and". For && you want Expression.AndAlso.
See also Expression.Or (|) vs Expression.OrElse (||).
Also, note that Expression != C# - it is language independent, so you might also see some cases where you don't get back (visually) what you would expect from C#.
Compare Expression.And:
Creates a BinaryExpression that represents a bitwise AND operation.
and Expression.AndAlso:
Creates a BinaryExpression that represents a conditional AND operation that evaluates the second operand only if the first operand evaluates to true.
And then compare that to your knowledge of the & and && operators.
The "&" operator and Expression.And represent a bitwise and. The "&&" operator and Expression.AndAlso represent a logical (and short-cutting) and operator.
Related
I've opted to implement the System.Linq.Dynamic namespace into my project where I rely on dynamically invoking LINQ expressions from a string against my underlying objects. This allows for highly configurable criteria at the data level.
string expression = "x.Client == 100 && x.Insurers.Any(it == 2 || it == 3)";
var x = new MyObject() { Client = 100, Insurers = new int[] { 1, 2 }};
var p = Expression.Parameter(typeof(MyObject), "x");
var e = System.Linq.Dynamic.DynamicExpression.ParseLambda(new[] { p }, null, expression);
bool result = e.Compile().DynamicInvoke(x); // True = condition met
My question is how do I dynamically identify the number of conditions each string expression contains so that I can give a weight to each expression and choose the one with the highest weight when overlapping occurs. Regex can work, but there must be something more efficient and practical such as an expression tree.
Ex.:
x.Client == 100 // Conditions = 1
x.Client == 100 && x.Insurers.Any(it == 3) // Conditions = 2
x.Client == 100 && x.Insurers.Any(it == 2 || it == 3) // Conditions = 3
I'm not familiar with the System.Linq.Dynamic library, but assuming it produces normal, strongly typed Expression trees you can use an ExpressionVisitor.
This one counts the number of boolean logical operations like &&:
int CountConditions(Expression expr)
{
var visitor = new CountBinaryOpsVisitor();
visitor.Visit(expr);
return visitor.BinaryOperationCount + 1;
}
class CountBinaryOpsVisitor : ExpressionVisitor
{
public int BinaryOperationCount { get; private set; }
protected override Expression VisitBinary(BinaryExpression node)
{
switch (node.NodeType)
{
case ExpressionType.And:
case ExpressionType.AndAlso:
case ExpressionType.Or:
case ExpressionType.OrElse:
case ExpressionType.ExclusiveOr:
// Don't count bitwise integer operations, if they are even supported?
if (node.Left.Type == typeof(bool))
BinaryOperationCount++;
break;
}
return base.VisitBinary(node);
}
}
An alternative approach would be to count the number of comparison operators (==, >= etc.), but I think that would need more complex code to handle boolean expressions like x.BooleanProp or x.Insurers.Any().
This implementation doesn't currently count conditional expressions (x ? y : z). Not sure how you would factor those into the number of conditions, especially when nested.
I'm having difficulty with a multiple option if statement.
Version 1 matches all without considering the && .contains("up")
if (
|| drow["ifName"].ToString().ToLower().Contains("vlan")
|| drow["ifName"].ToString().ToLower().Contains("st0")
|| drow["ifName"].ToString().ToLower().Contains("ge-0")
&& drow["ifStatus"].ToString().ToLower().Contains("up")
)
Version 2 matches none.
if ( (
|| drow["ifName"].ToString().ToLower().Contains("vlan")
|| drow["ifName"].ToString().ToLower().Contains("st0")
|| drow["ifName"].ToString().ToLower().Contains("ge-0")
)
&& drow["ifStatus"].ToString().ToLower().Contains("up")
Something I am missing?
Table looks basically like
ifName | ifStatus
vlan.0 | up
st0.1 | up
pp0.0 | up
ge-0/0/0 | down
EDIT:
So the goal is to match only rows that have ifStatus = UP, also changed table to clarify a real example.
What is your intended parse? logical OR (||) and logical AND (&&) are both left-associative and have different operator precedences:
http://msdn.microsoft.com/en-us/library/aa691323(v=vs.71).aspx
Logical AND binds more tightly than does logical OR, so, an expression like
A || B || C && D
parses as if it were written
A || B || ( C && D )
If that is your intent, you're good. If not, you'll need to add parentheses as needed to get the desired parse. My suspicion is that your intended parse is more like:
(A || B || C ) && D
But that is not how your original test parses.
As a good general rule, if you're mixing ANDs and ORs in a logical expression, always use parentheses to indicate your intent. Misunderstanding operator precedence in logical expressions is a major source of bugs.
This may not fix your problem, but it should make it easier to see what you're doing, as well as make the list more maintainable:
var matchNames = new[] {"a", "b", "c", "vlan.10"};
if (drow["ifStatus"].ToString().ToLower().Contains("up") //check up first, because it's cheaper
&& matchNames.Any(m => drow["ifName"].ToString().ToLower().Contains(m) )
{
//...
}
You can use linq to do this, for sample, add this namespace:
using System.Linq;
and try this:
string[] items = new[] { "vlan.10", "a", "b", "c" };
if (drop["IfStatus"].ToString().IndexIf("up", StringComparison.OrdinalIgnoreCase) > -1 &&
items.Any(x => drop["IfName"].ToString().IndexOf(x, StringComparison.OrdinalIgnoreCase) > -1)
{
// true...
}
Read about the Turkey Test, it shows why is important using the IgnoreCase method to compare instead Contains.
Using a regex here can simplify the logic.
if((Regex.IsMatch(drow["ifName"].ToString().ToLower(), "[abc]"))
&& (Regex.IsMatch(drow["ifStatus"].ToString().ToLower(), "up")))
{
}
in first case: it produce true if any one of the expression (OR Expression or AND Expression)evaluates to true.
Note : it is similar to if(A || B || C || D && E)
so if any OR Expressionin (A,B,C) evalutes to true or Expression D and E evaluates to true it becomes true.
in second case : it produce true if any one of the OR expressions is true and AND Expression drow["ifStatus"].ToString().ToLower().Contains("up") also must be true as you are using parenthesis pair.
Note : it is similar to if( (A || B || C || D) && (E) )
so if any one of the OR Expression(A,B,C,D) should evaluate to true and also AND expression E must be true to produce the result true.
Try This:
String name=drow["ifName"].ToString().ToLower();
Sting status=drow["ifStatus"].ToString().ToLower();
if ( (name.Contains("vlan.10") || name.Contains("a") || name.Contains("b")
|| name.Contains("c")) && (status.Contains("up")))
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)));
}
I am trying to understand what is happening in this variable assignment.
num = forward.Data.Key >= key ? 1 : 0;
In particular this part >= key ? 1 : 0
To help out forward is a LinkedListCell<KeyValuePair<int, double>> forward = _data.Next;
key is an int parameter being passed into the method.
Also it is a program written in C#
That's the ternary operator. It takes a boolean expression, and returns one of two values depending on the result of that expression. You get it in a number of languages.
It's equivalent to:
if( forward.Data.Key >= key ) {
num = 1;
}
else {
num = 0;
}
It is called ternary conditional operator. (or the short If-Else statement)
value = condition ? truePart : falsePart;
The ternary operator tests a condition. It compares two values. It produces a third value that depends on the result of the comparison.
from MSDN,
int input = Convert.ToInt32(Console.ReadLine());
string classify;
// if-else construction.
if (input < 0)
classify = "negative";
else
classify = "positive";
// ?: conditional operator.
classify = (input < 0) ? "negative" : "positive";
I'm using LINQ to create dynamic sql, when I'm using contains I don't want it to prefix and suffix % and if I'm using % inside my string I don't want to escape it. It escapes the percentage signs added by me using ~ as prefix before % as escape sequence character
For instance:
string str = '%test%.doc%'
.Contains(str) // converts this into LIKE '%~%test~%.doc~%%'
Expected Conversion: LIKE '%test%.doc%%'
as questioner asked, I've made my comments an answer
See Using LINQ Contains vs. SqlMethods.Like and in general the SqlMethods.Like method which will enable you to do a custom LIKE with Linq-to-sql.
Simple example:
var res = from row in dc.Table
where SqlMethods.Like(row.Column, "%A%A%")
select row;
More examples with Contains,StartsWith and Like: http://blogs.microsoft.co.il/blogs/bursteg/archive/2007/10/16/linq-to-sql-like-operator.aspx
Contains is probably translating into the use of the LIKE operator in SQL. This operator takes % as a wildcard character. Contains("abc") maps to LIKE '%abc%'.
I use the following extensions to avoid that case (although in my specific case, I'm still using wildcards, but you could modify it for your own effect).
public static bool Like(this string value, string term)
{
Regex regex = new Regex(string.Format("^{0}$", term.Replace("*", ".*")), RegexOptions.IgnoreCase);
return regex.IsMatch(value ?? string.Empty);
}
public static IEnumerable<string> Like(this IEnumerable<string> source, string expression)
{
return (from s in source where s.Like(expression) select s);
}
Unfortunately, I can't think of an easy way to do this, but this might work:
var a = from t in Db.Tests
let i1 = t.Name.IndexOf("test")
let i2 = t.Name.IndexOf(".doc")
where i1 != -1 && i2 != -1 && i1 < i2
select t;
Here is the equivalent in method chains:
Db.Tests.Select(t => new {t, i1 = t.Name.IndexOf("test")}).Select(
#t1 => new {#t1, i2 = #t1.t.Name.IndexOf(".doc")}).Where(
#t1 => #t1.#t1.i1 != -1 && #t1.i2 != -1 && #t1.#t1.i1 < #t1.i2).Select(#t1 => #t1.#t1.t);