LINQ Expression Tree Any() inside Where() - c#

I'm trying to generate the following LINQ query:
//Query the database for all AdAccountAlerts that haven't had notifications sent out
//Then get the entity (AdAccount) the alert pertains to, and find all accounts that
//are subscribing to alerts on that entity.
var x = dataContext.Alerts.Where(a => a.NotificationsSent == null)
.OfType<AdAccountAlert>()
.ToList()
.GroupJoin(dataContext.AlertSubscriptions,
a => new Tuple<int, string>(a.AdAccountId, typeof(AdAccount).Name),
s => new Tuple<int, string>(s.EntityId, s.EntityType),
(Alert, Subscribers) => new Tuple<AdAccountAlert, IEnumerable<AlertSubscription>> (Alert, Subscribers))
.Where(s => s.Item2.Any())
.ToDictionary(kvp => (Alert)kvp.Item1, kvp => kvp.Item2.Select(s => s.Username));
Using Expression Trees (which seems to be the only way I can do this when I need to use reflection and run-time types). Note that in the real code (see below) the AdAccountAlert is actually dynamic through reflection and a for-loop.
My problem: I can generate everything up to the .Where() clause. The whereExpression method call blows up because of incompatible types. Normally I know what to put there, but the Any() method call has me confused. I've tried every type I can think of and no luck. Any help with both the .Where() and .ToDictionary() would be appreciated.
Here's what I have so far:
var alertTypes = AppDomain.CurrentDomain.GetAssemblies()
.Single(a => a.FullName.StartsWith("Alerts.Entities"))
.GetTypes()
.Where(t => typeof(Alert).IsAssignableFrom(t) && !t.IsAbstract && !t.IsInterface);
var alertSubscribers = new Dictionary<Alert, IEnumerable<string>>();
//Using tuples for joins to keep everything strongly-typed
var subscribableType = typeof(Tuple<int, string>);
var doubleTuple = Type.GetType("System.Tuple`2, mscorlib", true);
foreach (var alertType in alertTypes)
{
Type foreignKeyType = GetForeignKeyType(alertType);
if (foreignKeyType == null)
continue;
IQueryable<Alert> unnotifiedAlerts = dataContext.Alerts.Where(a => a.NotificationsSent == null);
//Generates: .OfType<alertType>()
MethodCallExpression alertsOfType = Expression.Call(typeof(Enumerable).GetMethod("OfType").MakeGenericMethod(alertType), unnotifiedAlerts.Expression);
//Generates: .ToList(), which is required for joins on Tuples
MethodCallExpression unnotifiedAlertsList = Expression.Call(typeof(Enumerable).GetMethod("ToList").MakeGenericMethod(alertType), alertsOfType);
//Generates: a => new { a.{EntityId}, EntityType = typeof(AdAccount).Name }
ParameterExpression alertParameter = Expression.Parameter(alertType, "a");
MemberExpression adAccountId = Expression.Property(alertParameter, alertType.GetProperty(alertType.GetForeignKeyId()));
NewExpression outerJoinObject = Expression.New(subscribableType.GetConstructor(new Type[] { typeof(int), typeof(string)}), adAccountId, Expression.Constant(foreignKeyType.Name));
LambdaExpression outerSelector = Expression.Lambda(outerJoinObject, alertParameter);
//Generates: s => new { s.EntityId, s.EntityType }
Type alertSubscriptionType = typeof(AlertSubscription);
ParameterExpression subscriptionParameter = Expression.Parameter(alertSubscriptionType, "s");
MemberExpression entityId = Expression.Property(subscriptionParameter, alertSubscriptionType.GetProperty("EntityId"));
MemberExpression entityType = Expression.Property(subscriptionParameter, alertSubscriptionType.GetProperty("EntityType"));
NewExpression innerJoinObject = Expression.New(subscribableType.GetConstructor(new Type[] { typeof(int), typeof(string) }), entityId, entityType);
LambdaExpression innerSelector = Expression.Lambda(innerJoinObject, subscriptionParameter);
//Generates: (Alert, Subscribers) => new Tuple<Alert, IEnumerable<AlertSubscription>>(Alert, Subscribers)
var joinResultType = doubleTuple.MakeGenericType(new Type[] { alertType, typeof(IEnumerable<AlertSubscription>) });
ParameterExpression alertTupleParameter = Expression.Parameter(alertType, "Alert");
ParameterExpression subscribersTupleParameter = Expression.Parameter(typeof(IEnumerable<AlertSubscription>), "Subscribers");
NewExpression joinResultObject = Expression.New(
joinResultType.GetConstructor(new Type[] { alertType, typeof(IEnumerable<AlertSubscription>) }),
alertTupleParameter,
subscribersTupleParameter);
LambdaExpression resultsSelector = Expression.Lambda(joinResultObject, alertTupleParameter, subscribersTupleParameter);
//Generates:
// .GroupJoin(dataContext.AlertSubscriptions,
// a => new { a.AdAccountId, typeof(AdAccount).Name },
// s => new { s.EntityId, s.EntityType },
// (Alert, Subscribers) => new Tuple<Alert, IEnumerable<AlertSubscription>>(Alert, Subscribers))
IQueryable<AlertSubscription> alertSubscriptions = dataContext.AlertSubscriptions.AsQueryable();
MethodCallExpression joinExpression = Expression.Call(typeof(Enumerable),
"GroupJoin",
new Type[]
{
alertType,
alertSubscriptions.ElementType,
outerSelector.Body.Type,
resultsSelector.ReturnType
},
unnotifiedAlertsList,
alertSubscriptions.Expression,
outerSelector,
innerSelector,
resultsSelector);
//Generates: .Where(s => s.Item2.Any())
ParameterExpression subscribersParameter = Expression.Parameter(resultsSelector.ReturnType, "s");
MemberExpression tupleSubscribers = Expression.Property(subscribersParameter, resultsSelector.ReturnType.GetProperty("Item2"));
MethodCallExpression hasSubscribers = Expression.Call(typeof(Enumerable),
"Any",
new Type[] { alertSubscriptions.ElementType },
tupleSubscribers);
LambdaExpression whereLambda = Expression.Lambda(hasSubscribers, subscriptionParameter);
MethodCallExpression whereExpression = Expression.Call(typeof(Enumerable),
"Where",
new Type[] { joinResultType },
joinExpression,
whereLambda);

Please note: Everything after and including ToList() won't work on IQueryable<T> but on IEnumerable<T>. Because of this, there is no need to create expression trees. It certainly is nothing that is interpreted by EF or similar.
If you would look at the code that is generated by the compiler for your original query, you would see that it generates expression trees only until just before the first call to ToList.
Example:
The following code:
var query = new List<int>().AsQueryable();
query.Where(x => x > 0).ToList().FirstOrDefault(x => x > 10);
Is translated by the compiler to this:
IQueryable<int> query = new List<int>().AsQueryable<int>();
IQueryable<int> arg_4D_0 = query;
ParameterExpression parameterExpression = Expression.Parameter(typeof(int), "x");
arg_4D_0.Where(Expression.Lambda<Func<int, bool>>(Expression.GreaterThan(parameterExpression, Expression.Constant(0, typeof(int))), new ParameterExpression[]
{
parameterExpression
})).ToList<int>().FirstOrDefault((int x) => x > 10);
Please note how it generates expressions for everything up to ToList. Everything after and including it are simply normal calls to extension methods.
If you don't mimick this in your code, you will actually send a call to Enumerable.ToList to the LINQ provider - which it then tries to convert to SQL and fail.

It looks like, when constructing whereLambda, your second parameter should be subscribersParameter and not subscriptionParameter. At least, that would be the cause of your exception.

Related

OrderBy expression tree with dynamic field name, prioritising non-null values

I'm writing an extension method that takes a property name as a string and builds an expression tree to order by that column. It needs to support Linq-to-Entities as I'd like the order-by operation to be done in SQL. The method currently works as intended, however I'd now like to prioritise non-null values, i.e. always leave null values at the end of the collection, regardless of the order direction.
Here's the method I have so far:
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string orderBy, bool ascending) {
Type type = typeof(T);
ParameterExpression parameter = Expression.Parameter(type, "p");
PropertyInfo property = type.GetProperty(orderBy);
if (property == null) {
throw new ArgumentException("The given value did not match any available properties.", nameof(orderBy));
}
// Get the property accessor p.SortColumn
Expression propertyAccess = Expression.MakeMemberAccess(parameter, property);
// Get the lambda expression p => p.SortColumn
LambdaExpression orderByExp = Expression.Lambda(propertyAccess, parameter);
// Call the OrderBy(Descending) method with the above lambda expression
MethodCallExpression resultExp = Expression.Call(typeof(Queryable),
ascending ? "OrderBy" : "OrderByDescending", new[] { type, property.PropertyType },
source.Expression, Expression.Quote(orderByExp));
return (IOrderedQueryable<T>)source.Provider.CreateQuery<T>(resultExp);
}
If I didn't need to support Linq-to-Entities, I'd write something like this:
source.OrderBy(x => property.GetValue(x) == null).ThenBy/Descending(x => property.GetValue(x));
However, the Expression.Call()... syntax has thrown me as I don't fully understand what's happening behind the scenes there.
How would I go about prepending an OrderBy(value == null) operation to the expression tree?
Just order by equal to null first
var result = list.OrderBy(x => x.Name == null).ThenBy(x => x.Name)
Example
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> source, string propertyName, bool ascending)
{
var propertyInfo = typeof(T).GetProperty(propertyName) ?? throw new ArgumentException("The given value did not match any available properties.", nameof(propertyName));
var parameterExpression = Expression.Parameter(typeof(T), "p");
var propertyAccess = Expression.MakeMemberAccess(parameterExpression, propertyInfo);
var orderByNullLambda = Expression.Lambda(
Expression.Equal(propertyAccess, Expression.Constant(null)),
parameterExpression);
var resultExp = Expression.Call(
typeof(Queryable),
ascending ? "OrderBy" : "OrderByDescending",
new[] {typeof(T), typeof(bool)},
source.Expression,
Expression.Quote(orderByNullLambda));
var orderByPropertyLambda = Expression.Lambda(
propertyAccess,
parameterExpression);
var resultExp2 = Expression.Call(
typeof(Queryable),
ascending ? "ThenBy" : "ThenByDescending",
new[] {typeof(T), propertyInfo.PropertyType},
resultExp,
Expression.Quote(orderByPropertyLambda));
return (IOrderedQueryable<T>) source.Provider.CreateQuery<T>(resultExp2);
}
Usage
var list = new List<Bob>()
{
new() {Name = "Bsd"},
new() {Name = null},
new() {Name = "asd"}
};
var result = list.AsQueryable().OrderBy("Name", true).ToList();
foreach (var item in result)
Console.WriteLine(item.Name ?? "Null");
Result
asd
Bsd
Null

LINQ to Entities OrderBy Expression Tree

I am trying to write a LINQ query to orderBy a dynamic property given by a string value.
Here is what my original code was:
Expression<Func<T, dynamic>> orderBy = i => i.GetType().GetProperty("PropertyName").GetValue(null);
When I tried to run this orderBy I got the following exception:
LINQ to Entities does not recognize the method
'System.Object GetValue(System.Object)' method, and this method cannot
be translated into a store expression.
I am trying to work around this by creating an expression tree that will give me the same result. The code should be able to return any type depending on the parameter but I'm having trouble with the return type. If I don't convert the value I get a different error saying w Nullable DateTime can't be converted to Object. Here is the code that I have so far:
ParameterExpression pe = Expression.Parameter(typeof(T), "s");
Expression<Func<T, dynamic>> orderByExpression = Expression.Lambda<Func<T, dynamic>>(Expression.Convert(Expression.Property(pe, "PropertyName"), typeof(object)), pe);
and my new exception:
Unable to cast the type
'System.Nullable`1[[System.DateTime]]' to type
'System.Object'. LINQ to Entities only supports casting EDM primitive
or enumeration types.
How would I write this expression tree to do return a dynamic type? Also is there a better way I should be doing this in LINQ?
There are no template arguments for Expression<Func<T, TT>> which will allow you to represent both reference types and value types. Of course, you can build the expressions, but you must interact with them via reflection.
This will properly sort a collection:
IOrderedEnumerable<TEntityType> SortMeDynamically<TEntityType>(IEnumerable<TEntityType> query, string propertyname)
{
var param = Expression.Parameter(typeof(TEntityType), "s");
var prop = Expression.PropertyOrField(param, propertyname);
var sortLambda = Expression.Lambda(prop, param);
Expression<Func<IOrderedEnumerable<TEntityType>>> sortMethod = (() => query.OrderBy<TEntityType, object>(k => null));
var methodCallExpression = (sortMethod.Body as MethodCallExpression);
if (methodCallExpression == null)
throw new Exception("Oops");
var method = methodCallExpression.Method.GetGenericMethodDefinition();
var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
var orderedQuery = (IOrderedEnumerable<TEntityType>)genericSortMethod.Invoke(query, new object[] { query, sortLambda.Compile() });
return orderedQuery;
}
Or if you want it on an IQueryable (if you're using EF, for example)
IOrderedQueryable<TEntityType> SortMeDynamically<TEntityType>(IQueryable<TEntityType> query, string propertyname)
{
var param = Expression.Parameter(typeof(TEntityType), "s");
var prop = Expression.PropertyOrField(param, propertyname);
var sortLambda = Expression.Lambda(prop, param);
Expression<Func<IOrderedQueryable<TEntityType>>> sortMethod = (() => query.OrderBy<TEntityType, object>(k => null));
var methodCallExpression = (sortMethod.Body as MethodCallExpression);
if (methodCallExpression == null)
throw new Exception("Oops");
var method = methodCallExpression.Method.GetGenericMethodDefinition();
var genericSortMethod = method.MakeGenericMethod(typeof(TEntityType), prop.Type);
var orderedQuery = (IOrderedQueryable<TEntityType>)genericSortMethod.Invoke(query, new object[] { query, sortLambda });
return orderedQuery;
}

How to Create an Expression tree for .Where(x => x.<deep property>.Select(y => y.id).Intersect(List<int>).Any())

I'm creating a method that receives a Queryable<T> source, a string with a property name/path (could be a deep property for example "TrParent.DataTypes" to achieve this x => x.TrParent.DataTypes) and Enumerable<int> which holds the values I need to intersect.
Basically I come from the need to create the following query dynamically (I mean <DT_Det_Tr> and TrParent.DataTypes being know only at runtime, in the example DT_Det_Tr is not a type it is a class):
var _vals = new List<int>();
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);
Please keep in mind that the preceding query is just an example of what I need to achieve dynamically, what I really need is an expression tree that creates a predicate like the one shown above but using a dynamic type and with the deep navigation property specified within a string.
So, I'm using this function to create the expression for the deep property:
private static LambdaExpression CreateDelegateExpression<T>(out Type resultingtype, string property, string parameterName = "x")
{
var type = typeof(T);
ParameterExpression param = Expression.Parameter(type, parameterName);
Expression expr = param;
foreach (string prop in property.Split('.'))
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
Type delegateType = typeof(Func<,>).MakeGenericType(typeof(T), type);
LambdaExpression lambda = Expression.Lambda(delegateType, expr, param);
resultingtype = type;
return lambda;
}
And here is what I have so far for my function:
public static IQueryable<T> Intersect<T>(this IQueryable<T> source, string property, IEnumerable<int> value)
{
//List of ids
var _value = Expression.Constant(value);
//Get delegate expression to the deep property and it's inner type
Type type = null;
var lambda = CreateDelegateExpression<T>(out type, property, "x");
var enumtype = type.GetGenericArguments()[0];
ParameterExpression tpe = Expression.Parameter(enumtype, "y");
Expression propExp = Expression.Property(tpe, enumtype.GetProperty("Id"));
MethodInfo innermethod = typeof(Queryable).GetMethods().Where(x => x.Name == "Select").First();
//Error on next line...
var selectCall = Expression.Call(typeof(Queryable),
"Select",
new Type[] { enumtype, typeof(long) },
lambda,
propExp);
//TODO: Add rest of logic and actually filter the source
return source;
}
In the var selectCall = line I'm getting error:
No generic method 'Select' on type 'System.Linq.Queryable' is compatible with the supplied type arguments and arguments. No type arguments should be provided if the method is non-generic.
I've read a lot here on SO and other sites but I can't get past this part, I feel I'm going to bump into more trouble when I get to the .Intersect(List<int>).Any() part so any help on that also would be grand, thanks.
After a lot of thought, investigation and attempts I came up with a solution.
First, I made a simpler version of my goal query (the static example I used in my question), so instead of:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any()
);
I made this:
var res = dbContext.Set<DT_Det_Tr>()
.Where
(x => x.TrParent.DataTypes
.Any(y => _vals.Contains(y.Id))
);
Which is a lot easier to translate to expressions (or at least it was for me) because it omits the Select call.
I got rid of the method I was using to create the deep navigation property expression and streamlined it in my Intersect function, this was because it was doing some work I don't really need here plus I needed access to some of the variables I use inside it, then I made this:
public static IQueryable<T> Intersect<T>(this IQueryable<T> source, string property, IEnumerable<int> value)
{
var type = typeof(T);
var _value = Expression.Constant(value); //List of ids
//Declare parameter for outer lambda
ParameterExpression param = Expression.Parameter(type, "x");
//Outer Lambda
Expression expr = param;
foreach (string prop in property.Split('.')) //Dig for deep property
{
PropertyInfo pi = type.GetProperty(prop);
expr = Expression.Property(expr, pi);
type = pi.PropertyType;
}
//Get deep property's type
var enumtype = type.GetGenericArguments()[0];
//Declare parameter for inner lambda
ParameterExpression tpe = Expression.Parameter(enumtype, "y");
//Inner Collection lambda logic
//Property for inner lambda
Expression propExp = Expression.Property(tpe, enumtype.GetProperty("Id"));
//Contains method call .Contains(y.Id)
var containsMethodExp = Expression.Call(typeof(Enumerable), "Contains", new[] { propExp.Type }, _value, propExp);
//Create Expression<Func<enumtype, bool>>
var innerDelegateType = typeof(Func<,>).MakeGenericType(enumtype, typeof(bool));
//Create Inner lambda y => _vals.Contains(y.Id)
var innerFunction = Expression.Lambda(innerDelegateType, containsMethodExp, tpe);
//Get Any method info
var anyMethod = typeof(Enumerable).GetMethods().Where(m => m.Name == "Any" && m.GetParameters().Length == 2).Single().MakeGenericMethod(enumtype);
//Call Any with inner function .Any(y => _vals.Contains(y.Id))
var outerFunction = Expression.Call(anyMethod, expr, innerFunction);
//Call Where
MethodCallExpression whereCallExpression = Expression.Call
(
typeof(Queryable),
"Where",
new Type[] { source.ElementType },
source.Expression,
Expression.Lambda<Func<T, bool>>(outerFunction, new ParameterExpression[] { param })
);
//Create and return query
return source.Provider.CreateQuery<T>(whereCallExpression);
}
I hope this helps anyone trying to develop a similar solution.
Working with expression trees can be very hard and frustrating at first, but it's a really powerful tool once you get the hold of it.
If you have access to the dynamic keyword from c# 4.0, you might be able to work around the problem like this:
var _vals = new List<int>();
var res = dbContext.Set<DT_Det_Tr>()
.Where(obj => { dynamic x = obj;
return x.TrParent.DataTypes
.Select(t => t.Id)
.Intersect(_vals)
.Any();
}
);
But I don't know enough about the details of the problem you want to solve to say for sure.

Search through Where clause in LINQ, using dynamic properties

I'm basically trying to construct a query, and I don't know why microsoft made this so difficult in Entity Framework and LINQ. I have various parameter STRINGS. So if you see a variable, assume it's a string passed in from somewhere.
users = this.entities.tableUsers
.Where(searchfield+" LIKE %#0%", search)
.OrderBy(x => x.GetType().GetProperty(order_by).GetValue(x, null).ToString())
.Skip(Convert.ToInt32(limit_begin))
.Take(Convert.ToInt32(limit_end))
.ToList();
My question is what to put inside "Where()" function in LINQ.
I want to search a field with string "searchfield", for the value .contains() "search".
Not sure why Visual Studio won't let me do this easily.
I've tried this as well, no luck:
.Where(x => x.GetType().GetProperty(searchfield).GetValue(x, null).ToList().Contains(search))
Note: I don't want to install any new libraries, this should be incredibly easy and simple for a modern language. I don't mind if the query returns all the rows and I search through it AFTER with .Contains().
This is not trivial, but I believe it can be done. The following has not been tested. The code is borrowed from here.
Create a helper method somewhere like
public static Expression<Func<T, bool>> GetContainsExpression<T>(string propertyName, string containsValue)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var propertyExp = Expression.Property(parameterExp, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(propertyValue, typeof(string));
var containsMethodExp = Expression.Call(propertyExp, method, someValue);
return Expression.Lambda<Func<T, bool>>(containsMethodExp, parameterExp);
}
public static Expression<Func<T, TKey>> GetPropertyExpression<T, TKey>(string propertyName)
{
var parameterExp = Expression.Parameter(typeof(T), "type");
var exp = Expression.Property(parameterExp, propertyName);
return Expression.Lambda<Func<T, TKey>>(exp, parameterExp);
}
Use it like
users = this.entities.tableUsers
.Where(GetContainsExpression<User>(searchfield, search))
.OrderBy(GetPropertyExpression<User, string>(searchfield))
...
UPDATE
As an alternative, you could create extension methods to provide a cleaner syntax. Create the following methods in a static class somewhere:
public static IQueryable<T> WhereStringContains<T>(this IQueryable<T> query, string propertyName, string contains)
{
var parameter = Expression.Parameter(typeof(T), "type");
var propertyExpression = Expression.Property(parameter, propertyName);
MethodInfo method = typeof(string).GetMethod("Contains", new[] { typeof(string) });
var someValue = Expression.Constant(contains, typeof(string));
var containsExpression = Expression.Call(propertyExpression, method, someValue);
return query.Where(Expression.Lambda<Func<T, bool>>(containsExpression, parameter));
}
public static IOrderedQueryable<T> OrderBy<T>(this IQueryable<T> query, string propertyName)
{
var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
var parameter = Expression.Parameter(typeof(T), "type");
var propertyExpression = Expression.Property(parameter, propertyName);
var lambda = Expression.Lambda(propertyExpression, new[] { parameter });
return typeof(Queryable).GetMethods()
.Where(m => m.Name == "OrderBy" && m.GetParameters().Length == 2)
.Single()
.MakeGenericMethod(new[] { typeof(T), propertyType })
.Invoke(null, new object[] { query, lambda }) as IOrderedQueryable<T>;
}
public static IOrderedQueryable<T> OrderByDescending<T>(this IQueryable<T> query, string propertyName)
{
var propertyType = typeof(T).GetProperty(propertyName).PropertyType;
var parameter = Expression.Parameter(typeof(T), "type");
var propertyExpression = Expression.Property(parameter, propertyName);
var lambda = Expression.Lambda(propertyExpression, new[] { parameter });
return typeof(Queryable).GetMethods()
.Where(m => m.Name == "OrderByDescending" && m.GetParameters().Length == 2)
.Single()
.MakeGenericMethod(new[] { typeof(T), propertyType })
.Invoke(null, new object[] { query, lambda }) as IOrderedQueryable<T>;
}
Then you can call them like:
var users = this.entities.tableUsers.WhereStringContains(searchField, search)
.OrderBy(searchField);
this should be incredibly easy and simple for a modern language
No, it should not if it goes against that language paradigm. LINQ and Entity Framework (as well as any other decent ORM out there) are made precisely to avoid what you're trying to accomplish: non-typed and non-compiler-verifiable queries. So basically you're forcing square peg into round hole.
You can still take a look at Dynamic LINQ.
You'll have to build an expression tree to pass to the Where method. Here's a loose adaptation of some code I have lying about:
string searchfield, value; // Your inputs
var param = Expression.Parameter(typeof(User), "user");
return Expression.Lambda<Func<T, bool>>(
Expression.Call(
Expression.Property(
param,
typeof(User).GetProperty(searchfield)),
typeof(string).GetMethod("Contains"),
Expression.Constant(value)),
param);
That will generate an appropriate expression to use as the parameter to Where.
EDIT: FYI, the resultant expression will look something like user => user.Foo.Contains(bar).
EDIT: To sort, something like this (ripped from my DynamicOrderList class):
private IQueryable<T> OrderQuery<T>(IQueryable<T> query, OrderParameter orderBy)
{
string orderMethodName = orderBy.Direction == SortDirection.Ascending ? "OrderBy" : "OrderByDescending";
Type t = typeof(T);
var param = Expression.Parameter(t, "user");
var property = t.GetProperty(orderBy.Attribute);
return query.Provider.CreateQuery<T>(
Expression.Call(
typeof(Queryable),
orderMethodName,
new Type[] { t, typeof(string) },
query.Expression,
Expression.Quote(
Expression.Lambda(
Expression.Property(param, property),
param))
));
}
My answer to your other question about this:
When creating dynamic linq sorting and searching order statements in Entity Framework

How to produce a Subquery using non-generic Lambda

How would you translate the following generic Lambda function into a lambda expression :
context.AssociateWith<Product>(p => p.Regions.Where(r => r.Country == 'Canada')
I'm trying to create a full lambda expression without any <T> or direct call. Something like :
void AddFilter(ITable table, MetaDataMember relation)
{
var tableParam = Expression.Parameter(table.ElementType, "e");
var prop = Expression.Property(tableParam, relation.Name);
var func = typeof(Func<,>).MakeGenericType(table.ElementType, relation.type)
var exp = Expression.Lambda(func, prop, tableParam);
}
This will produce e.Regions... but I'm unable to get the Where part from there...
I know I'm very late in the game with my answer and likely this is not the exact solution you are looking for (still uses the frequently), but maybe it will help you and others building their expression:
/*
example: Session.Query.Where(m => m.Regions.Where(f => f.Name.Equals("test")))
*/
var innerItem = Expression.Parameter(typeof(MyInnerClass), "f");
var innerProperty = Expression.Property(innerItem, "Name");
var innerMethod = typeof(string).GetMethod("Equals", new[] { typeof(string) });
var innerSearchExpression = Expression.Constant(searchString, typeof(string));
var innerMethodExpression = Expression.Call(innerProperty, innerMethod, new[] { innerSearchExpression });
var innerLambda = Expression.Lambda<Func<MyInnerClass, bool>>(innerMethodExpression, innerItem);
var outerItem = Expression.Parameter(typeof(MyOuterClass), "m");
var outerProperty = Expression.Property(outerItem, info.Name);
/* calling a method extension defined in Enumerable */
var outerMethodExpression = Expression.Call(typeof(Enumerable), "Where", new[] { typeof(MyInnerClass) }, outerProperty, innerLambda);
var outerLambda = Expression.Lambda<Func<MyOuterClass, bool>>(outerMethodExpression, outerItem);
query = query.Where(outerLambda);
Based on an answer posted here: Creating a Linq expression dynamically containing a subquery.
Try this, it's not pretty but it gives you a valid expression for the whole structure. You could define the inner lambda as an expression but you would still have to compile it before you could pass it to Where(), so for the purposes of this answer it seems redundant.
Expression<Func<Product, IEnumerable<Region>>> getRegions =
p => p.Regions.Where(r => r.Country == "Canada");

Categories