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());
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);
I currently have the following code which allows me to call any method required on the EmailAddress property of my object, and it works great:
public static Expression<Func<T, bool>> BuildEmailAddressLambda(string method, params object[] args) {
var e = Expression.Parameter(typeof(T), "e");
var propertyInfo = typeof(T).GetProperty("EmailAddress");
var m = Expression.MakeMemberAccess(e, propertyInfo);
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType())).ToArray();
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(Expression.Call(m, mi, c), e);
return lambda;
}
// called:
lambda = LambdaExpressionHelper<MailingListMember>.BuildEmailAddressLambda("StartsWith", "r", StringComparison.OrdinalIgnoreCase);
However, I now need to amend the code so that it works for members of my object other than EmailAddress. Specifically, I would like to create a tree to cover the following expression, where multiple method calls are used:
e.GetStringValue(12).StartsWith("r", StringComparison.OrdinalIgnoreCase);
I have made several attempts, all of which end up in various errors. I feel I am missing something in the logic of how ExpressionTrees are created and some help with this would be greatly appreciated.
Thanks
Is this good for you?
public static Expression<Func<T, bool>> BuildEmailAddressLambda<T>(
string member, IEnumerable<object> memberArgs, string method, params object[] args)
{
var e = Expression.Parameter(typeof(T), "e");
var memberInfo =
(MemberInfo) typeof(T).GetField(member) ??
(MemberInfo) typeof(T).GetProperty(member) ??
(MemberInfo) typeof(T).GetMethod(member, (memberArgs ?? Enumerable.Empty<object>()).Select(p => p.GetType()).ToArray());
Expression m;
if (memberInfo.MemberType == MemberTypes.Method)
{
var a = memberArgs.Select(p => Expression.Constant(p));
m = Expression.Call(e, (MethodInfo) memberInfo, a);
}
else
{
m = Expression.MakeMemberAccess(e, memberInfo);
}
var mi = m.Type.GetMethod(method, args.Select(a => a.GetType()).ToArray());
var c = args.Select(a => Expression.Constant(a, a.GetType()));
return Expression.Lambda<Func<T, bool>>(Expression.Call(m, mi, c), e);
}
// called:
lambda = LambdaExpressionHelper<MailingListMember>.BuildEmailAddressLambda("EmailAddress", null, "StartsWith", "r", StringComparison.OrdinalIgnoreCase);
// or
lambda = LambdaExpressionHelper<MailingListMember>.BuildEmailAddressLambda("GetStringValue", new object[] { 12 }, "StartsWith", "r", StringComparison.OrdinalIgnoreCase);
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();