Prepare Dynamic Lambda Expression with Checking parentproperty null control - c#

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.

Related

How to implement a Linq NullCheck Expression?

I'm having a hard time trying to implement a nullcheck (isNull, isNotNull) expression using linq.
Here is part of my code:
//create a blog collection
var blogsCollection = new List<Blog>();
for (var i = 1; i <= 15; i++)
{
var b = new Blog()
{
BlogId = i,
Url = $"http://{i}.blog.com",
CreatedAt = DateTime.UtcNow.AddDays(-i),
};
blogsCollection.Add(b);
}
ParameterExpression parameterExpression = Expression.Parameter(typeof(Blog), "item");
Expression propertyExpression = Expression.Property(parameterExpression, "BlogId");
Expression targetExpression = Expression.Constant(null, propertyExpression.Type);
Expression exp = Expression.Equal(propertyExpression, targetExpression);
var filter=Expression.Lambda<Func<Blog, bool>>(exp, parameterExpression).Compile();
var blogs = (List<Blog>)blogsCollection.Where(filter);
This code throws an exception:
Argument types do not match
If I change the targetExpression to (without the type conversion):
Expression targetExpression = Expression.Constant(null);
Then it throws a different exception:
System.InvalidOperationException : The binary operator Equal is not defined for the types 'System.Int32' and 'System.Object'
Any Ideas about how to have a null check in Linq? I've checked here on SO, but none of them seems to work.
Your Id is of type System.Int32 which is a value type, so it is always not null (i.e. BlogId == null is always false). If you want to implement a generic filter which supports every possible input you can do something like that:
if (propertyExpression.Type.IsValueType && Nullable.GetUnderlyingType(propertyExpression.Type) == null)
{
filter = Expression.Lambda<Func<Blog, bool>>(Expression.Constant(false), parameterExpression).Compile();
}
else
{
// your current code
}

C# Expression trees - combine multiple method calls

I'm using .Net Core and expression trees.
I have a Product class, where LstProp property contains list of values separated by ';', like "val1;val2;val3;":
public class Product
{
// actually contains list of values separated by ';'
public string LstProp{get; set;}
}
And I want to filter Products by this property and contains any condition using expression trees.
I've try this, but it doesn't work.
var value="val1;val2"
var productItem = Expression.Parameter(typeof(Product), "product");
var prop = Expression.Property(productItem, "LstProp");
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var values = value.ToString().Split(';');
Expression predicate = null;
foreach (var val in values)
{
var listPropExpression = Expression.Constant(val);
var containsExpresion=Expression.Call(listPropExpression, method, property);
predicate = predicate != null ? Expression.Or(predicate, containsExpresion) : containsExpresion;
}
So I'm trying to combine call of Contains function for each value in the list, but getting error about "No conversion between BinaryExpression and MethodCallExpression".
How could I combine multiple method calls with expression trees?
I have found solution.
I got two problems:
Incorrect order of parameters.
Should be Expression.Call(property, method, listPropExpression); instead Expression.Call(listPropExpression, method, property);
Main problem was solved with simple cast:
predicate = predicate != null ? (Expression) Expression.Or(predicate, containsExpresion) : containsExpresion;
As a Result I get expression like product=>product.LstProp.Contains("val1") Or product.LstProp.Contains("val2")

Dynamic multiple where clause in linq c#

I have a request query using linq. The query has multiple where clause say return list of items matching name and city.
Below is the piece of code I used for multiple where clause, but it returns empty set of items.
wherefield contains list of field names like name;city
wherefieldValue contains list of field values like james;delhi
var where = FilterLinq<T>.GetWherePredicate(wherefield, wherefieldvalue).Compile();
items = items.Where(where).OrderByDescending(a => a.GetType().GetProperty(field).GetValue(a, null)).Skip
public class FilterLinq<T>
{
public static Expression<Func<T, Boolean>> GetWherePredicate(string whereFieldList, string whereFieldValues)
{
//the 'IN' parameter for expression ie T=> condition
ParameterExpression pe = Expression.Parameter(typeof(T), typeof(T).Name);
//combine them with and 1=1 Like no expression
Expression combined = null;
if (whereFieldList != null)
{
string[] field = whereFieldList.Split(';');
string[] fieldValue = whereFieldValues.Split(';');
for (int i = 0; i < field.Count(); i++)
{
//Expression for accessing Fields name property
Expression columnNameProperty = Expression.Property(pe, field[i]);
//the name constant to match
Expression columnValue = Expression.Constant(fieldValue[i]);
//the first expression: PatientantLastName = ?
Expression e1 = Expression.Equal(columnNameProperty, columnValue);
if (combined == null)
{
combined = e1;
}
else
{
combined = Expression.And(combined, e1);
}
}
}
//create and return the predicate
return Expression.Lambda<Func<T, Boolean>>(combined, new ParameterExpression[] { pe });
}
}
Your example works. Sure it works only for string properties, but i assume, that is your use case.
Perhaps it doesn't do what you want to achieve, becasue you combine your clause with and, but actually you do want to do it with Or, jus change this code line :
combined = Expression.And(combined, e1);
To
combined = Expression.Or(combined, e1);

Array to Binary Expression

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);

Parse boolean condition into expression tree

If I have a method that takes a boolean like:
public void Foo(boolean condition)
And call it like this:
Foo("MyField" == "MyValue");
Can I compose that into an expression tree in order to construct a query to some other datasource that will use MyField as one parameter and MyValue and another.
I can only seem to make that condition into a expression that evaluates to false.
UPDATE
var param = Expression.Parameter(typeof(Field), field);
var prop = Expression.PropertyOrField(param, "Name");
ConstantExpression #const = Expression.Constant(value, typeof(string));
var body = Expression.Equal(prop, #const);
var lambda = Expression.Lambda<Func<Field, bool>>(body, param);
Where Field is a class with two properties, Name and Value
Foo("MyField" == "MyValue") is, as noted at the bottom of the question, a constant false (right up at the compiler). You have a few choices here - the simplest of course is to do something like:
void Foo(Expression<Func<YourType,bool>> predicate) {...}
and call with
Foo(x => x.MyField == "MyValue");
then here, there is nothing left to do; we already have the expression. So I assume you mean "MyField" is a string only known at runtime, in which case:
void Foo<T>(string fieldName, T value) {
var param = Expression.Parameter(typeof(YourType), "x");
var body = Expression.Equal(
Expression.PropertyOrField(param, fieldName),
Expression.Constant(value, typeof(T))
);
var lambda = Expression.Lambda<Func<YourType, bool>>(body, param);
}
and call with Foo("MyField", "MyValue) (with an implicit <string> in there, courtesy of the compiler), or Foo("MyField", 123) if the prop is an int (implicit <int>),
The final scenario is where "MyValue" is also a string only known at runtime (emph: string) - in which case we'll need to parse it:
void Foo(string fieldName, string value) {
var param = Expression.Parameter(typeof(YourType), "x");
var prop = Expression.PropertyOrField(param, fieldName);
ConstantExpression #const;
if(prop.Type == typeof(string)) {
#const = Expression.Constant(value, typeof(string));
} else {
object parsed = TypeDescriptor.GetConverter(prop.Type)
.ConvertFromInvariantString(value);
#const = Expression.Constant(parsed, prop.Type);
}
var body = Expression.Equal(prop,#const);
var lambda = Expression.Lambda<Func<YourType, bool>>(body, param);
}
Here the call is always 2 strings - so Foo("MyField", "123") even when int.
In can create expression trees from delegates. For example, if you define your method so that it takes a delegate as a parameter, you can use it as follows:
public void Foo(Func<bool> fn)
{
// invoke the passed delegate
var result = fn();
}
Foo(() => "MyField" == "MyValue");
In order to create an expression tree, rather than execute the delegate, change the method as follows:
public void Foo(Expression<Func<bool>> expression)
{
// inspect your expression tree here
}
However, in your case, you will find that your expression is a boolean constant with a value of 'false', this is because the compiler has evaluated "MyField" == "MyValue" which is of course false.
If you just want name-value pairs, which not just use a Dictionary<string, string> ?

Categories

Resources