Custom lambda expression doesn't work as expected - c#

I'm making a lambda expression like this :
var property = typeof(Customer).GetProperty(inputArray[0], BindingFlags.Instance | BindingFlags.Public);
var parameter = Expression.Parameter(typeof(Customer));
var memberExpression = Expression.Property(parameter, property);
var eq = Expression.Equal(memberExpression, Expression.Constant(value));
//Combining eq with ANDs and ORs
var lambdaExpression = Expression.Lambda<Func<Customer, bool>>(eq, parameter);
// the lambda expression looks like this : {Param_0 => ((Param_0.NAME== "JASON") And (Param_0.NAME == "JASON"))}
var filteredCustomers = db.Customer.Where(lambdaExpression);
var list = filteredCustomers.ToList();
I can see that there are records whose name is JASON in the db. But list count is always zero. Can you tell me what the problem is? Thanks in advance.

For future visitors looking at this post,OP has trimmed the value removing extra spaces.
var param = Expression.Parameter(typeof(Customer), "p");
var memberExpression = Expression.PropertyOrField(param, "Your_Property_Name");
var body = Expression.Equal(memberExpression, Expression.Constant(value.Trim()));
var lambda = Expression.Lambda(body, param);

Related

c# How to evaluate compareTo(..) in Expression which returns a string

I have similar Situation like here: How to declare a Linq Expression variable in order to have it processed as a dbParameter
But in my case, don't want to compare with 'equal' operator, I need to compare one string against another. How can I achieve this?
I tried something like this:
var comparsionString = "aaa";
ParameterExpression param = Expression.Parameter(typeof(ItemSearch), "s");
Expression prop = Expression.Property(param, "Id");
Expression<Func<string>> idLambda = () => comparsionString;
Expression searchExpr = Expression.GreaterThanOrEqual(prop, idLambda.Body);
Expression<Func<ItemSearch, bool>> myLambda =
Expression.Lambda<Func<ItemSearch, bool>>(searchExpr, param);
But unfortunately GreaterThanOrEqual is not supported for strings. So I would need the string.CompareTo(..) method which is supported at least by SQL to Entities.
Say:
Expression searchExpr = Expression.IsTrue(*prop*.CompareTo(*idLambda*) >= 0);
How to write this in a way, that compiler can understand?
Any help is apreciated.
Thank you!
If you investigate the compiler translation for
Expression<Func<string, bool>> f = s => s.CompareTo("aaaa") >= 0;
You will discover you need to use GreaterThanOrEqual(Call("s", "CompareTo", "aaaa")) so the code to create that is:
var comparsionString = "aaa";
ParameterExpression param = Expression.Parameter(typeof(ItemSearch), "s");
Expression prop = Expression.Property(param, "Id");
Expression<Func<string>> idLambda = () => comparsionString;
var CallMethod = typeof(string).GetMethod("CompareTo", new[] { typeof(string) });
Expression callExpr = Expression.Call(prop, CallMethod, idLambda.Body);
Expression searchExpr = Expression.GreaterThanOrEqual(callExpr, Expression.Constant(0));
Expression<Func<ItemSearch, bool>> myLambda =
Expression.Lambda<Func<ItemSearch, bool>>(searchExpr, param);

The parameter '' was not bound in the specified LINQ to Entities query expression

I saw that there are many questions with similar title but none of them solved my problem.
I'm generating this lambda expression using a long and complex code which I don't want to paste here (I can paste if necessary) :
{Param_0 => (((Param_0.AD == "JASON") And (Param_1.AD == "JASON")) And (Param_2.STATE== "GEORGIA"))}
And I'm using this expression like this :
var cust= db.CUST.Where(lambdaExpression);
var custList= cust.ToList();
When it comes to ToList(), it gives this error :
The parameter '' was not bound in the specified LINQ to Entities query
expression.
What is wrong with my lambda expression? When I try something else, it works, but it doesn't work with this one. Thanks.
EDIT :
Here is my code :
var property = typeof(CUSTOMER).GetProperty(newArray[0], BindingFlags.Instance | BindingFlags.Public);
var parameter = Expression.Parameter(typeof(CUSTOMER));
var memberExpression = Expression.Property(parameter, property);
var value = Convert.ChangeType(newArray[1], property.PropertyType, CultureInfo.InvariantCulture);
var eq = Expression.Equal(memberExpression, Expression.Constant(value));
if (/*some condition*/)
{
foreach (var elem in someotherlist)
{
var newelem= elem.Replace(" ", string.Empty);
string[] newArray = newelem.Split(':');
var listproperty = typeof(CUSTOMER).GetProperty(newArray[0], BindingFlags.Instance | BindingFlags.Public);
var listparameter = Expression.Parameter(typeof(CUSTOMER));
var listmemberExpression = Expression.Property(listparameter, listproperty);
var listvalue = Convert.ChangeType(newArray[1], property.PropertyType, CultureInfo.InvariantCulture);
var eqnew = Expression.Equal(listmemberExpression, Expression.Constant(listvalue));
eq = Expression.And(eq, eqnew); // I guess problem is here.
}
}

How to declare a Linq Expression variable in order to have it processed as a dbParameter

I'm trying to create dynamic queries against Entity framework (or other Linq provider).
Let me explain my problem with some sample code.
If I hardcode an expression :
var id = 12345;
Expression<Func<ItemSearch, bool>> myLambda = (s) => s.Id == id;
var finalQuery = context.ItemSearch.Where(myLambda);
this.Log((finalQuery as ObjectQuery).ToTraceString());
The generated SQL looks like this :
SELECT ...
FROM ViewItemSearch "Extent1"
WHERE "Extent1".ID = :p__linq__0
with a nice :p__linq__0 dbParameter.
If I create an expression :
var id = 12345;
ParameterExpression param = Expression.Parameter(typeof(ItemSearch), "s");
Expression prop = Expression.Property(param, "Id");
Expression val = Expression.Constant(id);
Expression searchExpr = Expression.Equal(prop, val);
Expression<Func<ItemSearch, bool>> myLambda =
Expression.Lambda<Func<ItemSearch, bool>>(searchExpr , param);
var finalQuery = context.ItemSearch.Where(myLambda);
this.Log((finalQuery as ObjectQuery).ToTraceString());
The generated SQL looks like this :
SELECT ...
FROM ViewItemSearch "Extent1"
WHERE "Extent1".ID = 12345
No more :p__linq__0 dbParameter so the Db engine cannot cache query plans.
I understand that it is because I use
Expression val = Expression.Constant(id);
But I can't figure out how to bind a variable instead of the value.
You'll need to create a lambda at compile time to close over the id variable, to which you can then grab the body of an use in the composition of your more complex lambda:
var id = 12345;
ParameterExpression param = Expression.Parameter(typeof(ItemSearch), "s");
Expression prop = Expression.Property(param, "Id");
Expression<Func<int>> idLambda = () => id;
Expression searchExpr = Expression.Equal(prop, idLambda.Body);
Expression<Func<ItemSearch, bool>> myLambda =
Expression.Lambda<Func<ItemSearch, bool>>(searchExpr, param);

Expression method, default value for the body

I have this code :
var listExpression = new List<Expression>();
var parameter = Expression.Parameter(typeof(T));
var memberExpression = Expression.PropertyOrField(parameter, MyProperty);
listExpression.Add(
Expression.Call(
((MemberExpression)memberExpression), "Contains", null,
Expression.Constant((string)MyValue))
);
Expression body = Expression.Constant(true);
foreach (var expression in listExpression)
body = Expression.And(body, expression);
return Expression.Lambda<Func<T, bool>>(body, parameter);
The result of this is :
"True & $var1.AGE >= 5"
I don't have any problem when I use this predicate with Entity Framework but not work with NHiernate. I think the problem is the "True". Is it possible to create this predicate without this "True" ?
replace
Expression body = Expression.Constant(true);
foreach (var expression in listExpression)
body = Expression.And(body, expression);
by
var body = listExpression.First();//check first if listExpression.Any() would be better
listExpression.Skip(1).Aggregate(body, Expression.And);

Expression predicates with several parameters

I use the code gave on Stackoverflow by Marc Gravell here :
http://goo.gl/57nW2
The code :
var param = Expression.Parameter(typeof (Foo));
var pred = Expression.Lambda<Func<Foo, bool>>(
Expression.Call(
Expression.PropertyOrField(param, fieldName),
"StartsWith",null,
Expression.Constant(stringToSearch)), param);
Now, I'd like combine several argument, sample :
public void BuildPredicate(string[] typeSearch, string[] field, string searchValue)
{
//Content
//typeSearch = new string[] {"Contains", "StartsWith", "StartsWith" };
//field = new string[] { "FieldA", "FieldB", "FieldC" };
//FieldA contains searchValue and FieldB startWith searchValue and FieldC startWith searchValue
}
An idea ?
Thanks,
You can simply loop over all operations on all fields and build up a Expression tree containing an OrElse clause for each type/field combination
var expressions = from type in typeSearch
from field in fields
select Expression.Call(
Expression.PropertyOrField(param, field),
type, null,
Expression.Constant(stringToSearch));
Expression body = Expression.Constant(false);
foreach (Expression expression in expressions)
{
body = Expression.OrElse(body, expression);
}
var result = Expression.Lambda<Func<Foo, bool>>(body, param);
And as requested, an example including calls to ToUpper:
var expressions = from type in typeSearch
from field in fields
select Expression.Call(
Expression.Call(
Expression.PropertyOrField(param, field),
"ToUpper", null),
type, null,
Expression.Constant(stringToSearch));

Categories

Resources