I have 3 variables:
String propertyName = "Title";
String propertyValue = "Bob";
Type propertyType = typeof(String);
How can I construct Expression <Func<T, bool>>,
if T object has property Title?
I need Expression:
item => item.Title.Contains("Bob")
if propertyType is bool, then I need
item => item.OtherOproperty == false/true
and so on...
This code performs filtering and stores results in filtered array:
IQueryable<T> queryableData = (Items as IList<T>).AsQueryable<T>();
PropertyInfo propInfo = typeof(T).GetProperty("Title");
ParameterExpression pe = Expression.Parameter(typeof(T), "Title");
Expression left = Expression.Property(pe, propInfo);
Expression right = Expression.Constant("Bob", propInfo.PropertyType);
Expression predicateBody = Expression.Equal(left, right);
// Create an expression tree that represents the expression
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { queryableData.ElementType },
queryableData.Expression,
Expression.Lambda<Func<T, bool>>(predicateBody, new ParameterExpression[] { pe }));
T[] filtered = queryableData.Provider.CreateQuery<T>(whereCallExpression).Cast<T>().ToArray();
Related
I am trying to build following query
_dbContext.Models.Where(m => m.Children.Any(c => c.Name.Contains("Foobar")))
using expression trees. I am not very familar with expression trees yet, quite frankly I find them challenging. What I've tried:
var toCompare = "Foobar";
var param = Expression.Parameter(typeof(T));
// Properties
var propertyLeft = Expression.Property(param, "Children");
// Trying to retrieve Name property of the Child type
var propertyRight = Expression.Property(Expression.Parameter(propertyLeft.Type.GetGenericArguments().Single()), "Name");
// Methods
var contains = typeof(string).GetMethod("Contains", new[] { typeof(string) })!;
var any = typeof(Queryable).GetMethods()
.Where(m => m.Name == "Any")
.First(m => m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(string));
// Build c => c.Name.Contains(toCompare)
var expContains = Expression.Call(propertyRight, contains, Expression.Constant(toCompare, propertyRight.Type));
var expContainsLambda = Expression.Lambda<Func<T, bool>>(expContains, parameterExpression);
// Build m => m.Children.Any(...)
var expAny = Expression.Call(any, Expression.Constant(toCompare), expContainsLambda);
var expAnyLambda = Expression.Lambda<Func<Expression<Func<T, string>>, bool>>(expAny, parameterExpression);
// Build Models.Where(m => ...)
var expWhere = Expression.Lambda<Func<T, bool>>(expAnyLambda, param);
I am getting following error at expAny
System.ArgumentException: Expression of type 'System.String' cannot be used for parameter of type 'System.Linq.IQueryable`1[System.String]' of method 'Boolean Any[String](System.Linq.IQueryable`1[System.String], System.Linq.Expressions.Expression`1[System.Func`2[System.String,System.Boolean]])' (Parameter 'arg0')
Any ideas why? What changes are necessary to make it work?
Try the following:
var toCompare = "Foobar";
var param = Expression.Parameter(typeof(T), "m");
// m.Children
var propertyLeft = Expression.Property(param, "Children");
var childType = propertyLeft.Type.GetGenericArguments()[0];
var childParam = Expression.Parameter(childType, "c");
// c.Name
var propertyRight = Expression.Property(childParam, "Name");
// c => c.Name.Contains("Foobar")
var anyPredicate =
Expression.Lambda(
Expression.Call(propertyRight, nameof(string.Contains), Type.EmptyTypes, Expression.Constant(toCompare)),
childParam
)
// m.Children.Any(c => c.Name.Contains("Foobar"))
var anyCall = Expression.Call(typeof(Enumerable), nameof(Enumerable.Any), new [] { childType }, propertyLeft, anyPredicate);
// m => m.Children.Any(c => c.Name.Contains("Foobar"))
var expWhere = Expression.Lambda<Func<T, bool>>(anyCall, param);
(Disclaimer: I am the author of the library in question.)
I've written a library that produces various string representations from expression trees, such as C# or VB code, or Dynamic LINQ. One of the representations is the factory methods needed to create an expression tree. For example, this:
Expression<Func<Model, bool>> expr = m => m.Children.Any(c => c.Name.Contains("Foobar"));
Console.WriteLine(
expr.ToString("Factory methods", "C#")
);
produces this (using simulated types Model and ModelChild:
// using static System.Linq.Expressions.Expression
var c = Parameter(
typeof(ModelChild),
"c"
);
var m = Parameter(
typeof(Model),
"m"
);
Lambda(
Call(
typeof(Queryable).GetMethod("Any", new[] { typeof(IQueryable<ModelChild>), typeof(Expression<Func<ModelChild, bool>>) }),
MakeMemberAccess(m,
typeof(Model).GetProperty("Children")
),
Quote(
Lambda(
Call(
MakeMemberAccess(c,
typeof(ModelChild).GetProperty("Name")
),
typeof(string).GetMethod("Contains", new[] { typeof(string) }),
Constant("Foobar")
),
c
)
)
),
m
)
which you can then use to customize for your own needs.
How can I use type casting in Queryable collection which contains a Sql query in .net core. My expression is following which is returning just string value right now but I want to return value according to the datatype passed to it.
I have tried Expression.Convert type for Expression.Property but this is not supported in Sql.
Following is my expression where TSource is source which contains the property containing string value and TDataType is datatype to be converted into.
public static Expression<Func<TSource, TDataType>> CreateMapFromExpression<TSource, TDataType>(string fieldName, TypeCode dataType)
{
var parameterExpression = Expression.Parameter(typeof(TSource));
var collectionParameter = Expression.Property(parameterExpression, "CustomFieldValues");
var childType = collectionParameter.Type.GetGenericArguments()[0];
var propertyParameter = Expression.Parameter(childType, childType.Name);
var left = Expression.Property(propertyParameter, "Name");
var right = Expression.Constant(fieldName);
var innerLambda = Expression.Equal(left, right);
var innerFunction = Expression.Lambda(innerLambda, propertyParameter);
var method = typeof(Enumerable).GetMethods().Where(m => m.Name == "FirstOrDefault" && m.GetParameters().Length == 2).FirstOrDefault().MakeGenericMethod(typeof(CustomFieldValue));
var outerLambda = Expression.Call(method, Expression.Property(parameterExpression, collectionParameter.Member as System.Reflection.PropertyInfo), innerFunction);
var propertyGetter = Expression.Property(outerLambda, "Value");
var result = Expression.Lambda<Func<TSource, TDataType>>(propertyGetter, new ParameterExpression[] { parameterExpression });
return result;
}
I can do the following manually but need to build it up using System.Linq.Expressions.
.Where(x => x.OrganizationPersonRoles.Any(o => o.OrganizationId == value))
I can get it built up to the .Any and know I need to do an Expression.Call for the Any method but don't know how to build up the inner lambda (o => o.OrganizationId == value).
Expression exp = Expression.Parameter(typeof(T), "x");
Type type = typeof(T);
PropertyInfo pi = type.GetProperty("OrganizationPersonRoles");
exp = Expression.Property(exp, pi);
var lambda = Expression.Lambda(exp, arg);
Edit:
It's the ".Any" part I can't figure out how to build the expression. "OrganizationPersonRoles" is a collection on "Person". Something like:
var anyMethod = typeof(Queryable).GetMethods()
.Where(m => m.Name == "Any")
.Single(m => m.GetParameters().Length == 2)
.MakeGenericMethod(typeof(string));
var body = Expression.Call(exp, anyMethod, "Expression For Inner Lambda");
var lambda = Expression.Lambda(body, arg);
You can construct the lambda o => o.OrganizationId == value as follows.
int value = 5;
var xParameter = Expression.Parameter(typeof(T), "x");
var oParameter = Expression.Parameter(typeof(Organization), "o");
var expression =
Expression.Lambda(
Expression.Call(
typeof(Queryable), "Any", new[] { typeof(Organization) },
Expression.Property(xParameter, "OrganizationPersonRoles"),
Expression.Constant(
Expression.Lambda(
Expression.Equal(
Expression.Property(oParameter, "OrganizationId"),
Expression.Constant(value, typeof(int))),
oParameter),
typeof(Expression<Func<Organization, bool>>))),
xParameter);
This question already has answers here:
LINQ Expression Tree Any() inside Where()
(2 answers)
Closed 9 years ago.
I want to generate expression trees using the API for the following:
var managers = dataContext.Employees.Where(e => e.Subordinates.Any());
Additionally, how do I then generate the expression tree to do this:
var managedEmployees = managers.ToDictionary(key => key.Manager, value => value.Subordinates.Select(s => s.FullName));
I've come up with the following so far for the .Where(), but it errors because it doesn't like the type parameters in new Type[] { typeof(Func<Employee, IEnumerable<Employee>>) }.
ParameterExpression employeesParameter = Expression.Parameter(typeof(Employee), "e");
MemberExpression subordinatesProperty = Expression.Property(employeesParameter, typeof(Employee).GetProperty("Subordinates"));
MethodCallExpression hasSubordinates = Expression.Call(typeof(Enumerable),
"Any",
new Type[] { typeof(Employee) },
subordinatesProperty);
LambdaExpression whereLambda = Expression.Lambda(hasSubordinates, employeesParameter);
MethodCallExpression whereExpression = Expression.Call(typeof(Queryable),
"Where",
new Type[] { typeof(Func<Employee, IEnumerable<Employee>>) },
dataContext.Employees.AsQueryable(),
whereLambda);
I got this. The type parameters on Any and Where need to be Employee, not IQueryable<Employee> or IEnumerable<Employee> because it's just looking for the type parameters, not the actual types. I believe you also need an Expression.Constant(dataContext.Employees) instead of straight dataContext.Employees.
ParameterExpression employeesParameter = Expression.Parameter(typeof(Employee), "e");
MemberExpression subordinatesProperty = Expression.Property(employeesParameter, typeof(Employee).GetProperty("Subordinates"));
MethodCallExpression hasSubordinates = Expression.Call(typeof(Enumerable),
"Any",
new Type[] { typeof(Employee) },
subordinatesProperty);
LambdaExpression whereLambda = Expression.Lambda(hasSubordinates, employeesParameter);
MethodCallExpression whereExpression = Expression.Call(typeof(Queryable),
"Where",
new Type[] { typeof(Employee) },
Expression.Constant(dataContext.Employees),
whereLambda);
To call Any for your MemberExpression you should do this
ParameterExpression employeesParameter = Expression.Parameter(typeof(Employee), "e");
MemberExpression subordinatesProperty = Expression.Property(employeesParameter, typeof(Employee).GetProperty("Subordinates"));
var mi = typeof(Enumerable).GetMethods().First(x => x.Name == "Any" && x.GetParameters().Length == 1);
mi = mi.MakeGenericMethod(typeof (bool));
var hasSubordinates = Expression.Call(mi, subordinatesProperty);
and Where
var lambda = Expression.Lambda<Func<Employee, bool>>(hasSubordinates, employeesParameter);
var res = i.Where(lambda.Compile());
I have a method to Generate Expression By Clause as below:
internal static Expression<Func<TModel, T>> GenExpressionByClause<TModel, T>(string column)
{
PropertyInfo columnPropInfo = typeof(TModel).GetProperty(column);
var entityParam = Expression.Parameter(typeof(TModel), "e"); // {e}
var columnExpr = Expression.MakeMemberAccess(entityParam, columnPropInfo); // {e.column}
var lambda = Expression.Lambda(columnExpr, entityParam) as Expression<Func<TModel, T>>; // {e => e.column}
return lambda;
}
So I could build lambda expression as e=>e.column.
but I want to change it to e=>string.Format("{0}",e.column). How could I refactor the code?
This should do the work:
internal static Expression<Func<TModel, T>> GenExpressionByClause<TModel, T>(string column)
{
var columnPropInfo = typeof(TModel).GetProperty(column);
var formatMethod = typeof (string).GetMethod("Format", new[] {typeof (string), typeof (object)});
var entityParam = Expression.Parameter(typeof(TModel), "e");
var columnExpr = Expression.MakeMemberAccess(entityParam, columnPropInfo);
var formatCall = Expression.Call( formatMethod, Expression.Constant("{0}"), columnExpr);
var lambda = Expression.Lambda(formatCall , entityParam) as Expression<Func<TModel, T>>;
return lambda;
}
Note that you could cache the Format MethodInfo in a static field.
Final solution:
internal static Expression<Func<TModel, string>> GenExpressionByClauseEx<TModel>(string column)
{
var columnPropInfo = typeof(TModel).GetProperty(column);
var formatMethod = typeof(String).GetMethod("Format", new[] { typeof(string), typeof(Object) });
//string.Format(
var entityParam = Expression.Parameter(typeof(TModel), "e");
var columnExpr = Expression.MakeMemberAccess(entityParam, columnPropInfo);
var columnExprObj=Expression.Convert(columnExpr, typeof(object));
var formatCall = Expression.Call(formatMethod, Expression.Constant("111--{0}"), columnExprObj);
var lambda = Expression.Lambda(formatCall, entityParam) as Expression<Func<TModel, string>>;
return lambda;
}