How to use Contains method in Expression Class in C#? - c#

Here is my code as following:
protected Expression<Func<T, bool>> GetLambdaForField(string SearchTxtBox)
{
// Build a Lamda expression to get the Node headers for this sub master.
ConstantExpression constForeignKeyID = Expression.Constant(SearchTxtBox);
ParameterExpression paramEntity =
Expression.Parameter(typeof(T), "e");
MemberExpression mex =
LambdaExpression.PropertyOrField(paramEntity, DefaultSearchFieldName);
BinaryExpression filter = Expression.Equal(mex, constForeignKeyID);
Expression<Func<T, bool>> exprLambda =
Expression.Lambda<Func<T, bool>>(filter,
new ParameterExpression[] { paramEntity });
return exprLambda;
}
I have used Expression.Equal(...); but this is not what I actually wanted to achieve.
I wanted something like the Expression.Contains method (so that it can find the record with partial information without having me to enter the complete value).

You want to use Expression.Call method:
var filter = Expression.Call(mex,
typeof(string).GetMethod("Contains"),
constForeignKeyID);
This is equal to the following expression:
x => x.Prop.Contains(searchString)
Hope this helps.

Related

Is it possible to use a C# lambda expression to create an instance of System.Linq.Expressions.Expression at compile time?

A C# lambda expression (of type System.Linq.Expressions.Expression<TDelegate>) in code:
Expression<Func<Something, bool>> predicate = s => s.SomeProperty == 12;
To create a similar instance of System.Linq.Expressions.Expression:
var parameter = Expression.Parameter(typeof(Something), "s");
var property = Expression.Property(parameter, typeof(Something).GetProperty("SomeProperty"));
var constant = Expression.Constant(12);
var expression = Expression.Equal(property, constant);
Is there a way to declare expression given only the predicate? So without building the expression tree step by step in code, but having the compiler infer it from a lambda expression.
var expression = Expression.FromLambda<Something>(s => s.SomeProperty == 12);
Sure, just grab the body of the lambda expression, like so:
Expression FromLambda(Expression<Func<Something, bool>> lambda)
{
return lambda.Body;
}
Then you can use it like so:
var expression = FromLambda(s => s.SomeProperty == 12);
Just return the expression
Expression<Func<T, bool>> FromLambda<T>(Expression<Func<T, bool>> lambda) {
return lambda;
}
And use as desired
var expression = FromLambda<Something>(s => s.SomeProperty == 12);
This however is not very flexible and targets just this scenario. You would need to create methods for any other delegate you want to use.

"Does Not Contain" dynamic lambda expression

The code below does "Contain" expression:
private static Expression<Func<T, bool>> Contains<T>(string property, string value)
{
var obj = Expression.Parameter(typeof(T), "obj");
var objProperty = Expression.PropertyOrField(obj, property);
var contains = Expression.Call(objProperty, "Contains", null, Expression.Constant(value, typeof(string)));
var lambda = Expression.Lambda<Func<T, bool>>(contains, obj);
return lambda;
}
I am not very familiar with Expressions and I don't know how to put negation into the expression function and cannot find any suitable method in "Expression" class. Is there any similar way to create "Does Not Contain" expression dynamically?
A "does not contain" expression is exactly the same as a "does contain" expression - but wrapped with a unary negation expression. So basically you want:
// Code as before
var doesNotContain = Expression.Not(contains);
return Expression.Lambda<Func<T, bool>>(doesNotContain, obj);
Contains returns you a bool. Inverting a bool is done with the Expression.Not method.

lambda expression meaning/modificating

I'm having problem with a lambda expression which is used for custom filtering of data from DataGridView.
This is the expression:
private static Expression<Func<T, bool>> ExpressionLongEquals<T>(string Key, long Value)
{
var param = Expression.Parameter(typeof(T));
// create expression for param => param.TEntityNameId == PrimaryKey
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(param, Key),
Expression.Constant(Value)),
param);
return lambda;
}
The problem is that I have a case when the Value argument is of type long? which as it seems is acceptable but after completing this code I get an error that Method equal is not defined for Nullable1.System.Int64 and System.Int64. I have difficulties with understanding this method, not very sure that it's understandable for the others outside the context but however I'm gonna post my questions - first, what exactly is this, by which I mean - what I need to learn/read in order to be able with such kind of code like the one I posted and second. I'm pretty sure this method works with long values and make problems only when long? is passed as argument so is there any way to modify it to solve this problem?
The need of long?values is recent and this is what cause the problem generally I do this:
else if (property.PropertyType == typeof(long?))
{
long value = Convert.ToInt64(rule.Data);
selector = ExpressionLongEquals<T>(rule.Field, value);
}
but still I get the error about equal not defined for Nullable1.System.Int64 and System.Int64.
USE this...If I am correct you have to convert your value to a type of long?
private static Expression<Func<T, bool>> ExpressionLongEquals<T>(string Key, long Value)
{
var param = Expression.Parameter(typeof(T));
// create expression for param => param.TEntityNameId == PrimaryKey
var lambda = Expression.Lambda<Func<T, bool>>(
Expression.Equal(
Expression.Property(param, Key),
Expression.Constant(Value, typeof(long?)),
param);
return lambda;
}

Is there a work arround on this code that generates a NotSupportedException from NHibernate?

The following is the code:
list = query.Where(x => ((string)x.GetType()
.GetProperty(propertyName).GetValue(this, null))
.EndsWith(filterValue)).ToList();
This code is a refactored code that if I break it to its object and property level it would take me repeating it hundred times all around my code project. According to my research in this site it has something to do with the SQL translation. But is there any work around where this code or its variant will work fine?
The solution is to create an Expression that returns the specified property and pass that expression to Where:
var query = session.Query<YourType>();
list = query.Where(GetExpression<YourType>(propertyName, filterValue)).ToList();
GetExpression looks something like this:
public static Expression<Func<T, bool>> GetExpression<T>(string propertyName,
string filterValue)
{
var parameter = Expression.Parameter(typeof(T));
var property = Expression.Property(parameter, propertyName);
var method = typeof(string).GetMethod("EndsWith", new [] { typeof(string) });
var body = Expression.Call(property, method,
Expression.Constant(filterValue));
return (Expression<Func<T, bool>>)Expression.Lambda(body, parameter);
}

How do I dynamically create an Expression<Func<MyClass, bool>> predicate?

How would I go about using an Expression Tree to dynamically create a predicate that looks something like...
(p.Length== 5) && (p.SomeOtherProperty == "hello")
So that I can stick the predicate into a lambda expression like so...
q.Where(myDynamicExpression)...
I just need to be pointed in the right direction.
Update: Sorry folks, I left out the fact that I want the predicate to have multiple conditions as above. Sorry for the confusion.
Original
Like so:
var param = Expression.Parameter(typeof(string), "p");
var len = Expression.PropertyOrField(param, "Length");
var body = Expression.Equal(
len, Expression.Constant(5));
var lambda = Expression.Lambda<Func<string, bool>>(
body, param);
Updated
re (p.Length== 5) && (p.SomeOtherProperty == "hello"):
var param = Expression.Parameter(typeof(SomeType), "p");
var body = Expression.AndAlso(
Expression.Equal(
Expression.PropertyOrField(param, "Length"),
Expression.Constant(5)
),
Expression.Equal(
Expression.PropertyOrField(param, "SomeOtherProperty"),
Expression.Constant("hello")
));
var lambda = Expression.Lambda<Func<SomeType, bool>>(body, param);
Use the predicate builder.
http://www.albahari.com/nutshell/predicatebuilder.aspx
Its pretty easy!
To combine several predicates with the && operator, you join them together two at a time.
So if you have a list of Expression objects called predicates, do this:
Expression combined = predicates.Aggregate((l, r) => Expression.AndAlso(l, r));
To associate Lambda expression each other:
An other way is to use the following code. It s more flexible than the Schotime answer in my advice and work perfectly. No external Nuggets needed
Framework 4.0
// Usage first.Compose(second, Expression.And)
public static Expression<T> Compose<T>(this Expression<T> First, Expression<T> Second, Func<Expression, Expression, Expression> Merge)
{
// build parameter map (from parameters of second to parameters of first)
Dictionary<ParameterExpression,ParameterExpression> map = First.Parameters.Select((f, i) => new { f, s = Second.Parameters[i] }).ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with parameters from the first
Expression secondBody = ParameterRebinder.ReplaceParameters(map, Second.Body);
// apply composition of lambda expression bodies to parameters from the first expression
return Expression.Lambda<T>(Merge(First.Body, secondBody), First.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> First, Expression<Func<T, bool>> Second)
{
return First.Compose(Second, Expression.And);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> First, Expression<Func<T, bool>> second)
{
return First.Compose(second, Expression.Or);
}
public class ParameterRebinder : ExpressionVisitor
{
private readonly Dictionary<ParameterExpression, ParameterExpression> map;
public ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
I have a open-source project called Exprelsior that provides very simple ways to create dynamic predicates:
Based on your example:
var exp1 = ExpressionBuilder.CreateBinary<YourClass>("MyProperty.Length", 5, ExpressionOperator.Equals);
var exp2 = ExpressionBuilder.CreateBinary<YourClass>("SomeOtherProperty", "hello", ExpressionOperator.Equals);
var fullExp = exp1.And(exp2);
// Use it normally...
q.Where(fullExp)
It even supports full text predicate generation, so you can receive any dynamic query from an HTTP GET method, for example:
var exp1 = ExpressionBuilder.CreateBinaryFromQuery<YourClass>("eq('MyProperty.Length', '5')");
var exp2 = ExpressionBuilder.CreateBinaryFromQuery<YourClass>("eq('SomeOtherProperty', 'hello')");
var fullExp = exp1.And(exp2);
// Use it normally...
q.Where(fullExp)
It supports a lot more data types and operators.
Link: https://github.com/alexmurari/Exprelsior

Categories

Resources