How to convert an Expression<Func<T, bool>> to a Predicate<T> - c#

I have a method that accepts an Expression<Func<T, bool>> as a parameter. I would like to use it as a predicate in the List.Find() method, but I can't seem to convert it to a Predicate which List takes. Do you know a simple way to do this?
public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
var list = GetList<T>();
var predicate = [what goes here to convert expression?];
return list.Find(predicate);
}
Update
Combining answers from tvanfosson and 280Z28, I am now using this:
public IList<T> Find<T>(Expression<Func<T, bool>> expression) where T : class, new()
{
var list = GetList<T>();
return list.Where(expression.Compile()).ToList();
}

Func<T, bool> func = expression.Compile();
Predicate<T> pred = t => func(t);
Edit: per the comments we have a better answer for the second line:
Predicate<T> pred = func.Invoke;

Another options which hasn't been mentioned:
Func<T, bool> func = expression.Compile();
Predicate<T> predicate = new Predicate<T>(func);
This generates the same IL as
Func<T, bool> func = expression.Compile();
Predicate<T> predicate = func.Invoke;

I'm not seeing the need for this method. Just use Where().
var sublist = list.Where( expression.Compile() ).ToList();
Or even better, define the expression as a lambda inline.
var sublist = list.Where( l => l.ID == id ).ToList();

Related

Making generic expression generating method in C#

I am constructing an IQueryable query using several methods. The methods are somehow complex, but the problem I want to solve can be extracted and simplified as follows. I have two methods
private Expression<Func<T, bool>> CreateExpressionA(string ValueA)
{
return a => a.PropertyA.ToLower() == ValueA;
}
private Expression<Func<T, bool>> CreateExpressionB(string ValueB)
{
return a => a.PropertyB.ToLower() == ValueB;
}
and what I would rather have is this:
private Expression<Func<T, bool>> CreateExpression(??? Selector, string Value)
{
return a => a.Selector.ToLower() == Value;
}
or a similar approach that would allow me to avoid having two same methods with the only difference being in what property of an object is being used there.
Is it possible to do this in some elegant way?
You can pass in a selector Func that returns a string property:
private Expression<Func<T, bool>> CreateExpression<T>(Func<T, string> selector, string value)
{
return a => selector(a).ToLower() == value;
}
Usage:
CreateExpression<MyType>(x => x.PropertyA, "thevalue");
You could use reflection, more precisely the class PropertyInfo as an argument, but the implementation would be more involved. The method could be implemented as follows.
private Expression<Func<T, bool>> CreateExpression(PropertyInfo iInfo, string Value)
{
return a => (iInfo.GetPropertyValue(a) as string).ToLower() == ValueB;
}
However, note that this will work only if the type of the property is string, otherwise an additional type parameter could be used.
What you can do is create the Expression from scratch:
private Expression<Func<T, bool>> CreateExpression(string propertyName, string value) {
ParameterExpression param = Expression.Parameter(typeof(T));
MemberExpression property = Expression.Property(param, propertyName);
var valExpr = Expression.Constant(value);
var body = Expression.Equal(property, valExpr);
return Expression.Lambda<Func<T, bool>>(body, param);
}
Call with:
var expression = CreateExpression<TypeOfA>("PropertyA", "ValueForPropertyA");
It's a bit off the top of my head, but I think this should at least get you started. Let me know if you need help.

How to create Expression<Func<TSource, bool> by comparing Func<TSource, int> with int

I have a DbContext with a lot of DbSets. Every DbSet should have a function to get a page of items from the set, with a given pageSize and ordered by a specific sortOrder. Something like:
var pageItems = dbContext.Posts
.Where(post => post.BlogId == blogId)
.OrderBy(some sortorder)
.Skip(pageNr * pageSize)
.Take(pageSize);
I want to be able to do this with all my DbSets, so I have created an extension method where one of the parameters specifies the foreign key to compare and another the value this foreign key should have.
public static IQueryable<TSource> GetPage<TSource>(this IQueryable<TSource> source,
int pageNr, int pageSize,
Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue)
{
return source
.Where( ??? )
.OrderBy(some sortorder)
.Skip(pageNr * pageSize)
.Take(pageSize);
}
How to convert the keySelector in a predicate suitable for Where?
How to convert the keySelector in a predicate suitable for Where?
This is quite easy, but I have no idea how are you going to handle ordering. Anyway, here is how you can do what are you asking for:
public static IQueryable<TSource> GetPage<TSource, TKey>(this IQueryable<TSource> source,
int pageNr, int pageSize,
Expression<Func<TSource, TKey>> keySelector, TKey comparisonValue)
{
var predicate = Expression.Lambda<Func<TSource, bool>>(
Expression.Equal(keySelector.Body, Expression.Constant(comparisonValue)),
keySelector.Parameters);
return source
.Where(predicate)
//.OrderBy(some sortorder) ??
.Skip(pageNr * pageSize)
.Take(pageSize);
}
You're looking for a way to get an Expression<Func<TSource, boolean>> from an Expression<Func<TSource, Tproperty>> keySelector and a Tproperty comparisonValue in such a way that it can be translated in to a store expression by the Entity Framework.
That means that the trivial
public static Expression<Func<TSource, bool>> KeyPredicateNaive<TSource, Tproperty>(Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue)
{
return (TSource source) =>EqualityComparer<Tproperty>.Default.Equals(keySelector.Compile()(source), comparisonValue);
}
won't work. This can't be translated to a store expression.
We need to construct the expression manually. What we need is an equality expression with the key selector as its left value, and a constant expression with the comparison value as value as its right value. We can construct that as follows:
public static Expression<Func<TSource, bool>> KeyPredicate<TSource, Tproperty>(Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue)
{
var bd = Expression.Equal(keySelector.Body, Expression.Constant(comparisonValue));
return Expression.Lambda<Func<TSource, bool>>(bd, keySelector.Parameters);
}
The result of that can be passed to your where class. Slimmed down (so that it'll compile and run), your method will look like
public static IQueryable<TSource> GetPage<TSource>(this IQueryable<TSource> source,
int pageSize,
Expression<Func<TSource, Tproperty>> keySelector, Tproperty comparisonValue)
{
return source
.Where(KeyPredicate(keySelector, comparisonValue)
.Take(pageSize);
}
Would I use this? Probably not. It's easier all round to pass the predicate as a lambda to the function directly rather than constructing the expression yourself. But it's certainly a possibility.
try this and see if it helps
Expression<Func<TSource, bool>> keySelector
or simply
Func<TSource, bool> keySelector
Given this code:
sealed class SwapVisitor : ExpressionVisitor
{
private readonly Expression _from;
private readonly Expression _to;
public SwapVisitor(Expression from, Expression to)
{
_from = from;
_to = to;
}
public override Expression Visit(Expression node)
{
return node == _from ? _to : base.Visit(node);
}
}
static Expression<Func<TInput, bool>> Combine<TInput, TOutput>(
Expression<Func<TInput, TOutput>> transform,
Expression<Func<TOutput, bool>> predicate)
{
var swap = new SwapVisitor(predicate.Parameters[0], transform.Body);
return Expression.Lambda<Func<TInput, bool>>(
swap.Visit(predicate.Body), transform.Parameters);
}
You can:
.Where(Combine(keySelector, key => key == comparisonValue))
So that is creating a new Expression, with the body of the passed expression keySelector and the new expression for the comparison.
Thanks to Combine Lambda Expressions
You are writing extension to queryable source, yes? So just pass expressions and filter source:
public static IQueryable<TSource> GetPage<TSource, TKey>(this IQueryable<TSource> source,
Expression<Func<TSource, bool>> predicate,
Expression<Func<TSource, TKey>> keySelector,
int pageNr, int pageSize
)
{
return source
.Where(predicate)
.OrderBy(keySelector)
.Skip(pageNr * pageSize)
.Take(pageSize);
}
Usage:
db.Posts.GetPage(p => p.Author == "Bob", p => p.Date, 5, 10);
Note: in your approach you have problem with sorting (second expression) and all you get is passing two parameters p => p.Author, "Bob" instead of passing one ready-to use expression p => p.Author == "Bob".
But I would move predicate and keySelector out of GetPage method. Let this method focus on paging only (as method name states):
public static IQueryable<TSource> GetPage<TSource, TKey>(this IQueryable<TSource> source,
int pageNr, int pageSize)
{
return source.Skip(pageNr * pageSize).Take(pageSize);
}
Usage:
db.Posts.Where(p => p.Author == "Bob").OrderBy(p => p.Date).GetPage(5, 10);
Or if you have repository
postsRepository.GetByAuthor("Bob").GetPage(5, 10);

Add And statement to Expression<Func<T, bool>>

I have this function:
public List<T> Find(Expression<Func<T, bool>> query)
{
}
Find(x => x.Id == 4);
Inside the method Find I want to chain And Condition.
something like:
query.And(x => x.Secured == false);//Secured is a memeber inside T like Id.
Your problem is that you want to access a member of T within the generic method. T could be anything at this point so the compiler will not let you access Secured since T may not have a Secured member.
You could cast T to dynamic, but this just changes a compile time error to a runtime error (plus it's horrible).
The best way would be to ensure T implements some known interface that has a Secured member.
public List<T> Find(Expression<Func<T, bool>> query) where T : ISecured
The expression must be "opened" and rebuilt, like this:
public List<T> Find<T>(Expression<Func<T, bool>> query)
{
ParameterExpression parameter = query.Parameters[0];
Expression body = query.Body;
MemberExpression property = Expression.Property(parameter, "Secured");
body = Expression.AndAlso(body, Expression.Not(property));
Expression<Func<T, bool>> query2 = Expression.Lambda<Func<T, bool>>(body, parameter);
// Now you can use query2
return null;
}
Note that I'm considering this x.Secured == false to be equivalent to !x.Secured. Clearly Secured could be a strange class that overloads the == operator, but I'll ignore that case.
As suggested by #Ralf, you could even simply do two .Where. like:
public List<T> Find<T>(Expression<Func<T, bool>> query)
{
ParameterExpression parameter = query.Parameters[0];
MemberExpression property = Expression.Property(parameter, "Secured");
Expression<Func<T, bool>> query2 = Expression.Lambda<Func<T, bool>>(Expression.Not(property), parameter);
return context.Set<T>
.Where(query)
.Where(query2)
.ToList();
}
(I'm using as an example context.Set<T>, that is very similar to what you would do if you are using Entity Framework, but in general nearly all the IQuerable<>/IEnumerable<> treat two .Where() like a single .Where() with an && condition)
Something like
Find(x => x.Id == 4 && x.Secured == false);

Convert Expression<T, string>> to Expression<T, bool>>

so I want to make filtering work automaticly based on some easy settings. The code I have is this:
public ActionResult Index() // here I want to add filtering for Status I only want to show the active ones
{
IQueryable<Ticket> cases = db.Cases().AsQueryable();
cases = cases.EnablePaging().EnableFilterFor(x => x.Status);
return View(cases);
}
EnableFilterFor looks like this:
public static IQueryable<T> EnableFilterFor<T>(this IQueryable<T> queryable, Expression<Func<T, string>> keySelector)
{
string filterValue= "Active";
//Expression<Func<T, bool>> whereexpresion = keySelector.Compile() == "Active"
queryable = queryable.Where(
//here do the magic !! so that the result will be 'x=>x.Status == filterValue');
);
return queryable;
}
I googled a lot, tried many different things but no success. I somehow have to combine the keySelector and the filterValue to work (I need an Expression for the Where method to work). Any help would be greatly appreciated.
EDIT: After testing both solutions (thank you both!) I found out that Poke had the best one. Poke his code is the only code that doesn't change the way that the SQL is generated. When I took a look at Servy his generated SQL it always did an EXTRA Sql select query and an EXTRA and in the WHERE clause... No idea why :)
IQueryable.Where requires an Expression<Func<T, bool>>, so that will be the thing we need to build. As we want to integrate something from another expression (a Expression<Func<T, string>>), we have to build the expression “by hand”.
So in the end, we want to call LambdaExpression.Lambda<Func<T, bool>>(…) to get our expression for Where, but we need to fill in the expression body:
// first, we reuse the parameter from the `keySelector` expression
ParameterExpression param = keySelector.Parameters[0];
// The body is now just an equality comparison of the `keySelector`
// body, and the constant `filterValue`
Expression body = Expression.Equal(keySelector.Body, Expression.Constant(filterValue));
// now we just need to create a lambda expression for that body with the
// saved parameter and it’s all done:
queryable = queryable.Where(Expression.Lambda<Func<T, bool>>(body, param));
What we'll need here is a Compose method, for expressions. It'll take an expression that uses a value, and another expression that conceptually will use the result of the first expression as its input, generating a new output.
public static Expression<Func<TFirstParam, TResult>>
Compose<TFirstParam, TIntermediate, TResult>(
this Expression<Func<TFirstParam, TIntermediate>> first,
Expression<Func<TIntermediate, TResult>> second)
{
var param = Expression.Parameter(typeof(TFirstParam), "param");
var newFirst = first.Body.Replace(first.Parameters[0], param);
var newSecond = second.Body.Replace(second.Parameters[0], newFirst);
return Expression.Lambda<Func<TFirstParam, TResult>>(newSecond, param);
}
It will require the ability to replace one expression with another, which we can do using the following:
public static Expression Replace(this Expression expression,
Expression searchEx, Expression replaceEx)
{
return new ReplaceVisitor(searchEx, replaceEx).Visit(expression);
}
internal class ReplaceVisitor : ExpressionVisitor
{
private readonly Expression from, to;
public ReplaceVisitor(Expression from, Expression to)
{
this.from = from;
this.to = to;
}
public override Expression Visit(Expression node)
{
return node == from ? to : base.Visit(node);
}
}
Now we can write:
public static IQueryable<T> EnableFilterFor<T>(
this IQueryable<T> queryable,
Expression<Func<T, string>> keySelector)
{
string filterValue= "Active";
return queryable.Where(keySelector.Compose(status => status == filterValue));
}

How to pass a param list of PropertyExpression

I have a generic repository in which I'm trying to include a function that accepts a variable list of child tables to eagerly load. The function looks thus:
public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate, params Expression<Func<T, object>>[] includeEntities)
{
IQueryable<T> query = this._dbSet.Where(e => !e.Deleted).Where(predicate);
foreach (var entity in includeEntities)
{
query.Include(entity);
}
return query;
}
It works but I'm concerned about the object reference.
Using the function thus:
var foundEntities = Repository.Entities.FindBy(i => i.Id == targetId, i => i.Orders, i => i.Invoices);
The params passed in the includeEntites array are of type System.Linq.Expressions.PropertyExpression which is unfortunately an internal class so I can't make the function signature:
public IQueryable<T> FindBy(Expression<Func<T, bool>> predicate, params Expression<Func<T, System.Linq.Expressions.PropertyExpression>>[] includeEntities)
as I'd like. Any thoughts?

Categories

Resources