How to get build expression (c=>c.user.code == 'XXX') dynamically - c#

I'v build a method to construct expression dynamically, below is my code:
public static Expression<Func<T, bool>> BuildStringEqualLambda(string propertyName, string propertyValue)
{
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression propertyExp = Expression.Property(parameterExp, propertyName);
Expression right = Expression.Constant(propertyValue);
Expression e1 = Expression.Equal(propertyExp, right);
return Expression.Lambda<Func<T, bool>>(e1, new ParameterExpression[] { parameterExp });
}
But if the lambda like
c=>c.user.code == 'XXX'
and I invoke method like below:
BuildStringEqualLambda("user.code","XXX");
The method report error.
So my question is how to get build expression (c=>c.user.code == 'XXX') dynamically

Instead of:
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression propertyExp = Expression.Property(parameterExp, propertyName);
You need to go deeper for each property:
ParameterExpression parameterExp = Expression.Parameter(typeof(T), "type");
Expression propertyExp = parameterExp;
foreach (var property in propertyName.Split('.')) {
propertyExp = Expression.PropertyOrField(propertyExp, property);
}

Related

Expression Tree No method Where on type System.Linq.Queryable

I am testing dynamic expressions for a list but I have the following error.
CODE
List<DTOPreuab> modelolista = new List<DTOPreuab>();
modelolista.Add(new DTOPreuab { id = 1, nom = "test1" });
modelolista.Add(new DTOPreuab { id = 2, nom = "test2" });
IQueryable<DTOPreuab> queryableData = modelolista.AsQueryable<DTOPreuab>();
ParameterExpression pe = Expression.Parameter(typeof(string), "nom");
GetStringMethod
Expression left = Expression.Call(pe, typeof(string).GetMethod("ToLower", System.Type.EmptyTypes));
Expression right = Expression.Constant("test2");
Expression e1 = Expression.Equal(left, right);
var lambda = Expression.Lambda<Func<string, bool>>(e1, pe);
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { typeof(IEnumerable<DTOPreuab>) },
queryableData.Expression,
lambda
);
IQueryable<string> results = queryableData.Provider.CreateQuery<string>(whereCallExpression);
var response = results.ToArray();
Error Image

Writing lambda expression for Dictionary<string,string> member

i want to achieve this simple lambda with expressions.
x => x.myDictionary["key"] == "value"
This is I have done so far.
var parameterExpression = Expression.Parameter(typeof(T), "p");
var memberExpression = Expression.Property(parameterExpression, "myDictionary");
var expressionCall = Expression.Call(memberExpression, typeof(IDictionary<string, string>).GetMethod("get_Item"), Expression.Constant("key"));
var eq1 = Expression.Equal(expressionCall, Expression.Constant("value"));
var lambda = Expression.Lambda(eq1, parameterExpression);
q.Where(lambda);
But the filter is not working. Do you have any ideas?

Cannot convert from 'System.Linq.Expressions.LambdaExpression' to 'System.Linq.Expressions.Expression

I'm trying to implement expression tree with linq.I am getting error state as cannot convert lambdaexpression to expression. Please help i checked other solution but couldn't help as much ! Below is my code
ParameterExpression pe = Expression.Parameter(typeof(Person), "p");
var expr = Expression.Lambda(Expression.Property(pe, sortByProp), pe);
var d= expr.Compile();
IQueryable<Person> query = persons.AsQueryable();
List<Person> sortedList = query.OrderBy<Person, int>(expr).ToList();
It seems like you are trying to implement OrderBy dynamically using expression trees. You should try the following:
public static IQueryable<T> OrderBy<T>(this IQueryable<T> source, string sortProperty, ListSortDirection sortOrder)
{
var type = typeof(T);
var property = type.GetProperty(sortProperty);
var parameter = Expression.Parameter(type, "p");
var propertyAccess = Expression.MakeMemberAccess(parameter, property);
var orderByExp = Expression.Lambda(propertyAccess, parameter);
var typeArguments = new Type[] { type, property.PropertyType };
var methodName = sortOrder == ListSortDirection.Ascending ? "OrderBy" : "OrderByDescending";
var resultExp = Expression.Call(typeof(Queryable), methodName, typeArguments, source.Expression, Expression.Quote(orderByExp));
return source.Provider.CreateQuery<T>(resultExp);
}
and then you can call it as:
collection.OrderBy("Property on which you want to sort", ListSortDirection.Ascending);

Using ToString method at first in Expression Tree

I'm new to expression Tree and I need to convert the below lambda to Expression Tree
Data.Where(s => s.Property.ToString().StartsWith("My Search Data"));
However I have done upto
Data.Where(s => s.Property.StartsWith("My Search Data");
Now I Need to use the ToString Method before Using StartsWith.
Below is my sample code.
ParameterExpression e = Expression.Parameter(typeof(T), "e");
PropertyInfo propertyInfo = typeof(T).GetProperty(field);
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
ConstantExpression c = Expression.Constant(data, typeof(string));
MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
Expression call = Expression.Call(m, mi, c);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(call, e);
query = query.Where(lambda);
Consider using the overloads that allow you to access members by name instead. It will make this significantly easier to do.
// Data.Where(s => s.Property.ToString().StartsWith("My Search Data"));
var property = "Property";
var filter = "My Search Data";
var param = Expression.Parameter(typeof(T));
var body = Expression.Call(
Expression.Call(
Expression.Property(param, property),
"ToString",
null
),
"StartsWith",
null,
Expression.Constant(filter)
);
var lambda = Expression.Lambda<Func<T, bool>>(body, param);
The ideea is that you have to get "ToString" method from System.Object. Because it is a virtual method, the Runtime can dispatch the call on your real object.
Note: IData is your whatever data that has a property named "Property".
ParameterExpression e = Expression.Parameter(typeof(IData), "e");
PropertyInfo propertyInfo = typeof(IData).GetProperty("Property");
MemberExpression m = Expression.MakeMemberAccess(e, propertyInfo);
var toString = typeof (Object).GetMethod("ToString");
ConstantExpression c = Expression.Constant(data, typeof(string));
MethodInfo mi = typeof(string).GetMethod("StartsWith", new Type[] { typeof(string) });
var toStringValue = Expression.Call(m, toString);
Expression call = Expression.Call(toStringValue, mi, c);
Expression<Func<IData, bool>> lambda = Expression.Lambda<Func<IData, bool>>(call, e);

How do i expand on this expression?

I have this bit of code as an example, basically it spits out
p => p.fieldname.StartsWith("123")
But who would i expand on this to do something like this:
p => p.anotherentity.fieldname.StartsWith("123")
Here is a sample of the code i am have refactored for own needs:
string propertyName = "FirstName";
string methodName = "StartsWith";
string keyword = "123";
Type t = typeof (Person);
ParameterExpression paramExp = Expression.Parameter(t, "p");
// the parameter: p
MemberExpression memberExp = Expression.MakeMemberAccess(paramExp,
t.GetMember(propertyName).FirstOrDefault());
// part of the body: p.FirstName
MethodCallExpression callExp = Expression.Call(memberExp,
typeof (string).GetMethod(methodName,
new Type[] {typeof (string)}),
Expression.Constant(keyword));
// the body: p.FirstName.StartsWith("123")
Expression<Func<Person, bool>> whereExp = Expression.Lambda<Func<Person, bool>>(callExp, paramExp);
Expression<Func<Person, string>> selectExp = Expression.Lambda<Func<Person, string>>(memberExp, paramExp);
Console.WriteLine(whereExp); // p => p.FirstName.StartsWith("123")
Console.WriteLine(selectExp); // p => p.FirstName
To further explain let me show you what i would like to do:
public class Person
{
public string IdentityCode {get;set;}
public Loans Loans {get;set;}
}
public class Loans
{
public int Id {get;set;}
public Asset Assets {get;set;}
public Person person {get;set;}
}
public class Asset
{
public string SerialNumber {get;set;}
}
Then using an expression build something like this:
p => p.Loans.Asset.SerialNumber.StartsWith("123)
Or
p => p.Loans.Person.IdentityCode.StartsWith("123")
untested, but...
ParameterExpression paramExp = Expression.Parameter(t, "p"); // the parameter: p
MemberExpression memberExp =
Expression.MakeMemberAccess(paramExp, t.GetMember(propertyName).FirstOrDefault());
would become something like:
ParameterExpression paramExp = Expression.Parameter(t, "p"); // the parameter: p
MemberExpression otherEntityExp =
Expression.MakeMemberAccess(paramExp, t.GetMember("anotherentity").FirstOrDefault());
MemberExpression memberExp =
Expression.MakeMemberAccess(otherEntityExp, t.GetMember(propertyName).FirstOrDefault());
I'm not sure what you're asking for, updating an expression or building one from scratch...
If you already have the existing, old expression and want to update it, it would be very easy to create a new one. The idea is to dig through the expression tree down to the expression you want to replace. Then update all parent expressions with the newly replaced one.
Expression<Func<Obj, bool>> expr = p => p.fieldname.StartsWith("123");
var body = expr.Body as MethodCallExpression; // *.StartsWith()
var obj = body.Object as MemberExpression; // p.fieldname
var param = expr.Parameters.First(); // p
var newAccess = Expression.PropertyOrField(param, "anotherentity"); // p.anotherentity
var newObj = obj.Update(newAccess); // update obj
var newBody = body.Update(newObj, body.Arguments); // update body
var newExpr = expr.Update(newBody, expr.Parameters);// update expr
Otherwise to build up the expression tree:
Expression<Func<Person, bool>> expr =
p => p.Loans.Asset.SerialNumber.StartsWith("123");
Work it out from the beginning.
var p = Expression.Parameter(typeof(Person), "p");
var accessLoans = Expression.PropertyOrField(p, "Loans");
var accessAsset = Expression.PropertyOrField(accessLoans, "Asset");
var accessSerialNumber = Expression.PropertyOrField(accessAsset, "SerialNumber");
var callArgs = new Expression[] { Expression.Constant("123", typeof(string)) };
var callStartsWith = Expression.Call(accessSerialNumber, "StartsWith", null, callArgs);
var newExpr = Expression.Lambda<Func<Person, bool>>(callStartsWith, p);
I'll leave the last one as an exercise for you.

Categories

Resources