Array to Binary Expression - c#

Given x => x.LastName,
How do I convert something like .Where({"Doe", "Don", "Donna"}.Contains(x.LastName))?
I need to convert this .Contains Expression into
.Where(x => x.LastName == "Doe" || x.LastName == "Don" || x.LastName == "Donna")
So basically given an array {"Doe", "Don", "Donna"} and a Member Expression x.LastName, how do I dynamically build a valid BinaryExpression as above?
Okay so a little background, I am trying to build a LINQ interface to a NoSQL database that has no idea how to handle an Enumerable.Contains MemberCallExpression. So I am trying to translate that Enumerable.Contains into a simple OrElse Expression that the Database can handle.
I can get x.LastName from the MemberCallExpression's Arguments[0], and I have figured out how to get an Enumerable of Constants from the Expression, that I have been able to build a List<BinaryExpression> out of, by enumerating the Constants and saying
Expressions.Add(Expression.Equal(node.Arguements[0], Expression.Constant(item)));
How do I take that list of BinaryExpressions and build a valid BinaryExpression of the form Expressions[0] OrElse Expressions[1] OrElse Expressions[2].
I tried:
BinaryExpression expression = Expressions[0];
for (var idx = 1; idx < Expressions.Count - 1; idx++)
{
expression += Expression.OrElse(Expressions[idx], Expressions[idx +1]);
}
However += is not valid on a BinaryExpression. And I am unsure how to actually append another Binary Expression onto an existing BinaryExpression...

I'll leave my previous answer for future reference, because I think Expression construction is not easy so that example may be useful for someone. As to the problem, chaining expressions is quite simple. you should use the Expression.OrElse method for that:
BinaryExpression expression = Expressions[0];
for (var idx = 1; idx < Expressions.Count - 1; idx++)
{
expression = Expression.OrElse(expression, Expressions[idx]);
}

string[] arr = {"Doe", "Don", "Donna"};
BinaryExpression exp = null;
MemberExpression member = ... //get the "x.LastName" expression
foreach (String name in arr) {
BinaryExpression eq = Expression.Equal(member, name);
if (exp == null) {
exp = eq;
}
else {
exp = Expression.OrElse(exp, eq);
}
}
Type delegateType = typeof(Func<T, bool>); //T is your entity type, e.g. Person
ParameterExpression arg = ... //the "x" in "x.LastName"
//construct the lambda expression x => [expr].
LambdaExpression lambda = Expression.Lambda(delegateType, expr, arg);
then, you can feed that to .Where():
.Where(lambda);

Related

How to build composite expression in c#?

I need to build an expression for the following:
numberings(n=>configs.All(c=>c.Field1!=n.Field1 || c.Field2!=n.Field2 || ...))
I tried doing something like this:
BinaryExpression expression = null;
foreach (var criteria in SelectionCriteria)
{
Expression<Func<Numbering, TConfiguration, bool>> exp = (_, c) => criteria.ConfigurationField(c) != criteria.NumberingField(_);
expression = expression == null ? Expression.MakeBinary(ExpressionType.OrElse, exp, exp) : Expression.OrElse(expression, exp);
}
if (expression == null) return Result.Failure(string.Format(Errors.NumberingFieldSelectionNotDefined, $"{shipper}:{GetType().FullName}"));
var lambda = Expression.Lambda<Func<TConfiguration, bool>>(expression);
numberingsToRemove = numberingsToRemove.Where(_ => configsThatStay.All(lambda));
And here is where I got stuck. Compiler says:
The binary operator OrElse is not defined for the types 'System.Func<TNumbering,TConfiguration,System.Boolean> and 'System.Func<TNumbering,TConfiguration,System.Boolean>
Can someone help me solve this problem?
If you have numbered fields like that, enough that you need to think about using a ... ellipsis when describing them, you should think about a collection property like a List or Array instead. That would allow you to use a Zip() operation to compare them:
numberings.Select(n => configs.All(c => c.Fields.Zip(n.Fields, (c1,n1) => c1!=n1).All());

Prepare Dynamic Lambda Expression with Checking parentproperty null control

I have dynamic lambda expression tree,I prepare my expression and compile it.
My problem is when I prepare expression something like
MyClass1.Property2.ChildProperty1=="xyz";
if Property2 is null then I take nullrefence error.
public class MyClass1
{
public string Property1{get;set;}
public MyClass2 Property2{get;set;}
}
public class MyClass2
{
public string ChildProperty1{get;set;}
public string ChildProperty2{get;set;}
}
public Func<T, bool> CompileRule<T>(Rule r)
{
...
Func<T, bool> myfunc =Expression.Lambda<Func<T, bool>>(expr, paramUser).Compile();
...
return myfunc;
}
Another function I execute my request like " myfunc(myrequest); "
Is there a generic way to check null control when I preprare my expression in CompileTime,If ParentProperty null then return false for these statement?In compile method I dont have entity ,so I have to prepare statement with check null control
So you want to create dynamically a body of an expression like this:
Expression<Func<MyClass1, string>> expr =
x => x.Property2?.ChildProperty1;
Unfortunately currently there is not standard Expression method supporting that. If you try the above, you'll get a compile time error:
Error CS8072 An expression tree lambda may not contain a null propagating operator.
One possible way is to generate a body of an expression like this instead:
Expression<Func<MyClass1, string>> expr =
x => x.Property2 != null ? x.Property2.ChildProperty1 : null;
It's not the exact equivalent, but should work.
Here is a sample snippet which does that dynamically:
string memberPath = ...; // e.g. "Property2.ChildProperty2"
var parameter = Expression.Parameter(typeof(T), "x");
Expression result;
if (memberPath.IndexOf('.') < 0)
result = Expression.PropertyOrField(parameter, memberPath);
else
{
var memberNames = memberPath.Split('.');
var members = new Expression[memberNames.Length];
for (int i = 0; i < memberNames.Length; i++)
members[i] = Expression.PropertyOrField(i > 0 ? members[i - 1] : parameter, memberNames[i]);
result = members[members.Length - 1];
// For non nullable value types, promote the result into the corresponding nullable type
if (result.Type.IsValueType && Nullable.GetUnderlyingType(result.Type) == null)
result = Expression.Convert(result, typeof(Nullable<>).MakeGenericType(result.Type));
var nullResult = Expression.Constant(null, result.Type);
for (int i = members.Length - 2; i >= 0; i--)
{
result = Expression.Condition(
Expression.NotEqual(members[i], Expression.Constant(null)),
result, nullResult);
}
}
At the end, the result variable contains the expression that you can use for left side of the comparison predicate.

How to use the parenthesis in the expression tree

[question]
Could you tell me how to create the appropriate expression tree?
[detail]
I created following query statically, and result is as expected.
// Datasource
string[] dataSource = { "John", "Ken", "Mark", "Mary", "Tom" };
// Keyword for Search
string[] keywords = { "o", "Mark", "Tom" };
//LINQ Query (Static)
var query = dataSource.AsQueryable().
Where(item =>
(item.ToLower().Contains(keywords[0].ToLower()) ||
item.ToLower().Contains(keywords[1].ToLower())) &&
item.ToLower().Contains(keywords[2].ToLower())).
OrderByDescending(item => item);
//Result
// "Tom"
condition A || condition B && condition C
but I do not know how to code the following condition with expression tree
(condition A || condition B) && condition C
Does anyone tell me how to use the parethesis in the expression tree?
So far what I created the body for lambda expression is as follows which does not work well.
public static Expression GetContainsExpression(Expression parameter, string keyword, Expression curBody)
{
var keywordValue = Expression.Constant(keyword, typeof(string));
var newBody =
Expression.Equal( Expression.Call(parameter, ToLower), Expression.Call(keywordValue, ToLower) );
///Connect curBody Expression and newBody Expression with "Or" e.s. ||
if (curBody != null)
{
if (keyword == "Tom")
{
return Expression.AndAlso(curBody, newBody);
}
else
return Expression.OrElse(curBody, newBody);
}
return newBody;
}
The parethesis are created automaticaly. You can't avoid it. Expression.OrElse or Expression.AndAlso takes an other expression for left and right and if they are combined expressions as BinaryExpression is they are wrapped in parethesis automatically.
Have a look at this code:
var paramX = Expression.Parameter(typeof(bool), "x");
var paramY = Expression.Parameter(typeof(bool), "y");
var paramZ = Expression.Parameter(typeof(bool), "z");
var expr = Expression.AndAlso(Expression.OrElse(paramX, paramY), paramZ);
If you call expr.ToString() you'll get "((x OrElse y) AndAlso z)". Even the outer AndAlso-Expression is wrapped into parethesis. There is no way to remove them (as I know so far).
A small hint: You can call ToString() on every expression and it will return the created code. Knowing this makes it easier to create dynamic expressions because you've a small ability to see what you get.

Dynamic linq query expression tree for sql IN clause using Entity framework

I want to create a dynamic linq expression for sql IN clause in EF 6.0 with code first approch. Note that i am new to Expressions. What i want to achive is
select * from Courses where CourseId in (1, 2, 3, 4)
//CourseId is integer
The normal linq query looks like this. But i want to query it dynamically
string[] ids = new string[]{"1", "2", "3", "4"};
var courselist = DBEntities.Courses.Where(c => ids.Contains(SqlFunctions.StringConvert((decimal?)c.CourseId)))
There are two ways to make dynamic expression.
1) one ways is to loop through ids and make expressions
The below code will create the following expression in debug view
{f => ((StringConvert(Convert(f.CourseId)).Equals("23") Or StringConvert(Convert(f.CourseId)).Equals("2")) Or StringConvert(Convert(f.CourseId)).Equals("1"))}
Dynamic Expression is
var param = Expression.Parameters(typeof(Course), "f")
MemberExpression property = Expression.PropertyOrField(param, "CourseId");
MethodInfo mi = null;
MethodCallExpression mce = null;
if (property.Type == typeof(int))
{
var castProperty = Expression.Convert(property, typeof(double?));
var t = Expression.Parameter(typeof(SqlFunctions), "SqlFunctions");
mi = typeof(SqlFunctions).GetMethod("StringConvert", new Type[] { typeof(double?) });
mce = Expression.Call(null,mi, castProperty);
}
mi = typeof(string).GetMethod("Equals", new Type[]{ typeof(string)});
BinaryExpression bex = null;
if (values.Length <= 1)
{
return Expression.Lambda<Func<T, bool>>(Expression.Call(mce, mi, Expression.Constant(values[0]), param));
}
//var exp1 = Expression.Call(mce, mi, Expression.Constant(values[0]));
for (int i = 0; i < values.Length; i++)
{
if (bex == null)
{
bex = Expression.Or(Expression.Call(mce, mi, Expression.Constant(values[i])), Expression.Call(mce, mi, Expression.Constant(values[i + 1])));
i++;
}
else
bex = Expression.Or(bex, Expression.Call(mce, mi, Expression.Constant(values[i])));
}//End of for loop
return Expression.Lambda<Func<T, bool>>(bex, param);
2) The 2nd way that i tried (debug view)
{f => val.Contains("23")} //val is parameter of values above
The dynamic expression for above that i tried is
var param = Expression.Parameters(typeof(Course), "f")
MemberExpression property = Expression.PropertyOrField(param, "CourseId");
var micontain = typeof(Enumerable).GetMethods().Where(m => m.Name == "Contains" && m.GetParameters().Length == 2).Single().MakeGenericMethod(typeof(string));
var mc = Expression.Call(micontain, Expression.Parameter(values.GetType(), "val"), Expression.Constant("2"));//NOTE: I haven't use CourseId for now as i am getting conversion error
return Expression.Lambda<Func<T, bool>>(mc, param);
I get the following errors
LINQ to Entities does not recognize the method 'System.String StringConvert(System.Nullable`1[System.Double])' method, and this
method cannot be translated into a store expression when i use the
first methodology. I know i can't use ToString with EF thats why I used SqlFunctions but it is not working for me.
The parameter 'val' was not bound in the specified LINQ to Entities query expression using 2nd methodology
I am trying this from last 4 days. I googled it but didn't find any suitable solution. Please help me.
After a lot of struggle I found solution to my question.
I want to achieve this sql query
select * from Courses where CourseId in (1, 2, 3, 4)
Using Linq to Entities, but I want to pass in(1,2,3,4) list dynamically to linq query. I created an Extension class for that purpose.
public static class LinqExtensions
{
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> In<T, TValue>(this Expression<Func<T, bool>> predicate,string propertyName, List<TValue> values)
{
var param = predicate.Parameters.Single();
MemberExpression property = Expression.PropertyOrField(param, propertyName);
var micontain = typeof(List<TValue>).GetMethod("Contains");
var mc = Expression.Call(Expression.Constant(values), micontain, property);
return Expression.Lambda<Func<T, bool>>(mc, param);
}
}
Use of LinqExtensions
var pred = LinqExtensions.False<Course>(); //You can chain In function like LinqExtensions.False<Course>().In<Course, int>("CourseId", inList);
var inList= new List<int>(){1, 2, 3}; //Keep in mind the list must be of same type of the Property that will be compared with. In my case CourseId is integer so the in List have integer values
pred =pred.In<Course, int>("CourseId", inList); //TValue is int. As CourseId is of type int.
var data = MyEntities.Courses.Where(pred);
I hope this might be beneficial for some one
have you seen the type of
var courselist = DBEntities.Courses.Where(c => ids.Contains(c.CourseId)))
above statement would not return actual list of courses. The query is not executed yet. It just returns IQuereable. The query is executed when you actually call .ToList() method on it
so, your solution is..
Create array of IDs using for loop and then simply run the below query
var courselist = DBEntities.Courses.Where(c => ids.Contains(c.CourseId))).ToList()

From BinaryExpression to Expression<Func<T, bool>>

Suppose I have something like
Expression<Func<SomeType, DateTime>> left = x => x.SomeDateProperty;
Expression<Func<SomeType, DateTime>> right = x => dateTimeConstant;
var binaryExpression = Expression.GreaterThan(left, right);
Expression<Func<SomeType, bool>> predicate =
x => x.SomeDateProperty> dateTimeConstant;
1) How can I replace the right hand of the assignment of the last line with something that uses the binaryExpression instead? var predicate = x => binaryExpression; doesn't work.
2) The right is always a constant, not necessarily DateTime.Now. Could it be of some simpler Expression type? For instance, it doesn't depend on SomeType, it is just a constant.
3) If I have the GreaterThan as a string, is there a way to get from this string to the method with the same name in Expression? In general, if the name of the comparison method is given as a string, how can I go from the string to actually calling the method with the same name on the Expression class?
It has to work with LINQ to Entities, if it matters.
1 and 2: You need to build the expression tree manually to do this, the compiler cannot help because it only constructs ready-made expressions that represent functions in their entirety. That's not useful when you want to build functions piece by piece.
Here's one straightforward way to build the expression you want:
var argument = Expression.Parameter(typeof(SomeType));
var left = Expression.Property(argument, "SomeDateProperty");
var right = Expression.Constant(DateTime.Now);
var predicate = Expression.Lambda<Func<SomeType, bool>>(
Expression.GreaterThan(left, right),
new[] { argument }
);
You can take this for a test drive with
var param = new SomeType {
SomeDateProperty = DateTime.Now.Add(TimeSpan.FromHours(-1))
};
Console.WriteLine(predicate.Compile()(param)); // "False"
3: Since in all likelihood the number of possible choices for your binary predicate will be quite small, you could do this with a dictionary:
var wordToExpression =
new Dictionary<string, Func<Expression, Expression, BinaryExpression>>
{
{ "GreaterThan", Expression.GreaterThan },
// etc
};
Then, instead of hardcoding Expression.GreaterThan in the first snippet you would do something like wordToExpression["GreaterThan"](left, right).
Of course this can also be done the standard way with reflection.
When you use GreaterThan, you need to specify the expression bodies, not the lambda itself. Unfortunately, there is a complication: the x in the two expressions is not the same.
In this particular case, you could just about get away with this, because the second expression does not use x
So; your "1" and "2" should be answered by:
var binaryExpression = Expression.GreaterThan(left.Body, right.Body);
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression,
left.Parameters);
However, to handle this in the general case, you must rewrite on of the expressions, to fix-up the parameters:
var binaryExpression = Expression.GreaterThan(left.Body,
new SwapVisitor(right.Parameters[0], left.Parameters[0]).Visit(right.Body));
var lambda = Expression.Lambda<Func<SomeType, bool>>(binaryExpression,
left.Parameters);
with:
public class SwapVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public SwapVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
For your "3"; there is nothing inbuilt for that; you could use reflection, though:
string method = "GreaterThan";
var op = typeof(Expression).GetMethod(method,
BindingFlags.Public | BindingFlags.Static,
null, new[] { typeof(Expression), typeof(Expression) }, null);
var rightBody = new SwapVisitor(right.Parameters[0],
left.Parameters[0]).Visit(right.Body);
var exp = (Expression)op.Invoke(null, new object[] { left.Body, rightBody });
var lambda = Expression.Lambda<Func<SomeType, bool>>(exp, left.Parameters);

Categories

Resources