Writing lambda expression for Dictionary<string,string> member - c#

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?

Related

Expression builder for GroupBy Lamda for each property

I want to populate the itemsource of a ComboBox with items from my List, depending on which property from T is selected.
The statement should be like:
foreach property which is a string,
select the values of the property, make distinct.
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
var queryableData = data.AsQueryable();
var paramExp = Expression.Parameter(typeof(T), "left");
foreach (var pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
var callExpr = Expression.MakeMemberAccess(paramExp, pi);
var lambdaExpr = Expression.Lambda(callExpr) ;
// From here on it goes wrong!!!
var comleteExpr = lambdaExpr as Expression<Func<T, bool>>;
var compiledExpr = comleteExpr.Compile();
var res = data.Select(compiledExpr).Distinct().ToList();
// add to results ...
}
return null;
}
The problem seems to be the casting from the lambda expression to prepare for compilation.
Thank you for your help.
First of all you need to provide paramExp to lambda. Secondly there is generic version of Lamda method which is just easier to use. Finally, you don't need to compile expression when you use IQueryable. You created queryableData variable and didn't use it.
Here is code:
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
var queryableData = data.AsQueryable();
var paramExp = Expression.Parameter(typeof(T), "left");
foreach (var pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
var callExpr = Expression.MakeMemberAccess(paramExp, pi);
var lambdaExpr = Expression.Lambda<Func<T, bool>>(callExpr, paramExp);
var res = queryableData.Select(lambdaExpr).Distinct().ToList();
// add to results ...
}
return null;
}
I think you should check if the casting result is not null :
public Dictionary<string, List<string>> CreateSuggestionsLists<T>(List<T> data)
{
IQueryable<T> queryableData = data.AsQueryable();
ParameterExpression paramExp = Expression.Parameter(typeof(T), "left");
foreach (PropertyInfo pi in typeof(T).GetProperties().Where(p => p.PropertyType == typeof(string)))
{
MemberExpression callExpr = Expression.MakeMemberAccess(paramExp, pi);
LambdaExpression lambdaExpr = Expression.Lambda(callExpr);
// From here on it goes wrong!!!
if (!(lambdaExpr is Expression<Func<T, bool>> comleteExpr)) continue;
Func<T, bool> compiledExpr = comleteExpr.Compile();
List<bool> res = data.Select(compiledExpr).Distinct().ToList();
// add to results ...
}
return null;
}

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

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

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