I have code
public static class PredicateExtensions
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.Or(expression1.Body, invokedExpression), expression1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
var invokedExpression = Expression.Invoke(expression2, expression1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>(Expression.And(expression1.Body, invokedExpression), expression1.Parameters);
}
}
How to use this code instead of LINQ Union and Intersect methods ?
If you have a union and intersection of the form:
var union = source.Where(predicate0).Union(source.Where(predicate1));
var inter = source.Where(predicate0).Intersect(source.Where(predicate1));
Then union will have the values for which either predicate0 or predicate1 was true while inter will have the values for which both predicate0 and predicate1 were true.
For this reason, the following would have the same result:
var union = source.Where(predicate0.Or(predicate1));
var inter = source.Where(predicate0.And(predicate1));
This depends upon the components of the union and intersection being produced by two Where queries though. In the likes of:
var union = source1.Where(predicate0).Select(item => item.ID)
.Union(source2.Where(predicate1).Select(item => item.ID));
Then it's likely that the predicates aren't even of the same type, and there are yet other cases where combining predicates will not work as a replacement of Union and Intersect.
It is though useful to be able to consider the first examples both ways, in terms of one's understanding of how predicates, unions and intersections work.
Related
I'm trying to merge two predicates in then Any clause of the following code, but i cannot find a way to do this.
private static Expression<Func<Order, bool>> BuildWhereExpression(DataFilterOrder filter, AppDbContext dbContext)
{
var predicate = PredicateBuilder.True<Order>();
var confirmationPredicate = PredicateBuilder.True<HConfirmation>();
if (!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Number == filter.ConfirmationNumber);
}
if (filter.ConfirmationDateFrom != null)
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Date >= filter.ConfirmationDateFrom);
}
.....
predicate = predicate.And(o =>
dbContext.Confirmations
.Join(
dbContext.DocumentHierarchies,
c => c.DocumentId,
h => h.ChildDocumentId,
(c, h) => new HConfirmation { Confirmation = c, Hierarchy = h })
.Any(r => r.Hierarchy.ParentDocumentId == o.DocumentId &&
???confirmationPredicate???)
return predicate;
}
....
// called by
var wherePredicate = BuildWhereExpression(filter, dbContext);
var list = await dbContext.Orders
.Where(wherePredicate)
.ToListAsync();
Any help? Thanks very much.
PredicateBuilder class:
public static class PredicateBuilder
{
// Creates a predicate that evaluates to true.
public static Expression<Func<T, bool>> True<T>() { return param => true; }
// Creates a predicate that evaluates to false.
public static Expression<Func<T, bool>> False<T>() { return param => false; }
// Creates a predicate expression from the specified lambda expression.
public static Expression<Func<T, bool>> Create<T>(Expression<Func<T, bool>> predicate) { return predicate; }
public static Expression<Func<T, bool>> Create1<T, K>(Expression<Func<T, bool>> predicate, K obj) { return predicate; }
// Combines the first predicate with the second using the logical "and".
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.AndAlso);
}
// Combines the first predicate with the second using the logical "or".
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.OrElse);
}
// Negates the predicate.
public static Expression<Func<T, bool>> Not<T>(this Expression<Func<T, bool>> expression)
{
var negated = Expression.Not(expression.Body);
return Expression.Lambda<Func<T, bool>>(negated, expression.Parameters);
}
// Combines the first expression with the second using the specified merge function.
static Expression<T> Compose<T>(this Expression<T> first, Expression<T> second, Func<Expression, Expression, Expression> merge)
{
// zip parameters (map from parameters of second to parameters of first)
var map = first.Parameters
.Select((f, i) => new { f, s = second.Parameters[i] })
.ToDictionary(p => p.s, p => p.f);
// replace parameters in the second lambda expression with the parameters in the first
var secondBody = ParameterRebinder.ReplaceParameters(map, second.Body);
// create a merged lambda expression with parameters from the first expression
return Expression.Lambda<T>(merge(first.Body, secondBody), first.Parameters);
}
class ParameterRebinder : ExpressionVisitor
{
readonly Dictionary<ParameterExpression, ParameterExpression> map;
ParameterRebinder(Dictionary<ParameterExpression, ParameterExpression> map)
{
this.map = map ?? new Dictionary<ParameterExpression, ParameterExpression>();
}
public static Expression ReplaceParameters(Dictionary<ParameterExpression, ParameterExpression> map, Expression exp)
{
return new ParameterRebinder(map).Visit(exp);
}
protected override Expression VisitParameter(ParameterExpression p)
{
ParameterExpression replacement;
if (map.TryGetValue(p, out replacement))
{
p = replacement;
}
return base.VisitParameter(p);
}
}
}
Well, tried to simplify solution, but looks like it is needed to build Any part dynamically. Sorry not tested and some small mistakes can be here.
private static Expression<Func<Order, bool>> BuildWhereExpression(DataFilterOrder filter, AppDbContext dbContext)
{
var predicate = PredicateBuilder.True<Order>();
var confirmationPredicate = PredicateBuilder.True<HConfirmation>();
if (!string.IsNullOrWhiteSpace(filter.ConfirmationNumber))
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Number == filter.ConfirmationNumber);
}
if (filter.ConfirmationDateFrom != null)
{
confirmationPredicate = confirmationPredicate.And(r =>
r.Confirmation.Document.Date >= filter.ConfirmationDateFrom);
}
.....
// we can write this query separately
var confirmations = dbContext.Confirmations
.Join(dbContext.DocumentHierarchies,
c => c.DocumentId,
h => h.ChildDocumentId,
(c, h) => new HConfirmation { Confirmation = c, Hierarchy = h }
);
var orderParam = Expression.Parameter(typeof(Order), "o");
var hConfirmationParam = Expression.Parameter(typeof(HConfirmation), "r");
// r.Hierarchy.ParentDocumentId == o.DocumentId
var anyPredicate = (Expression)Expression.Equal(Expression.Property(Expression.Property(hConfirmationParam, "Hierarchy"), "ParentDocumentId"),
Expression.Property(orderParam, "DocumentId"));
// r.Confirmation
var confirmationAccess = Expression.Property(hConfirmationParam, "Confirmation");
// correcting confirmation predicate
var confirmationPredicateCorrected = ExpressionReplacer.GetBody(confirmationPredicate, confirmationAccess);
// r.Hierarchy.ParentDocumentId == o.DocumentId && confirmationPredicateCorrected
anyPredicate = Expression.AndAlso(anyPredicate, confirmationPredicateCorrected);
// r => r.Hierarchy.ParentDocumentId == o.DocumentId && confirmationPredicateCorrected
var anyLambda = Expression.Lambda(anyPredicate, hConfirmationParam);
var anyCall = Expression.Call(typeof(Queryable), "Any", new [] { typeof(HConfirmation) }, confirmations.Expression, Expression.Quote(anyLambda));
var additionalPredicate = Expression.Lambda<Func<Order, bool>>(anyCall, orderParam);
predicate = predicate.And(additionalPredicate);
return predicate;
}
Anyawy, additional helper class is needed:
public class ExpressionReplacer : ExpressionVisitor
{
readonly IDictionary<Expression, Expression> _replaceMap;
public ExpressionReplacer(IDictionary<Expression, Expression> replaceMap)
{
_replaceMap = replaceMap ?? throw new ArgumentNullException(nameof(replaceMap));
}
public override Expression Visit(Expression exp)
{
if (exp != null && _replaceMap.TryGetValue(exp, out var replacement))
return replacement;
return base.Visit(exp);
}
public static Expression Replace(Expression expr, Expression toReplace, Expression toExpr)
{
return new ExpressionReplacer(new Dictionary<Expression, Expression> { { toReplace, toExpr } }).Visit(expr);
}
public static Expression Replace(Expression expr, IDictionary<Expression, Expression> replaceMap)
{
return new ExpressionReplacer(replaceMap).Visit(expr);
}
public static Expression GetBody(LambdaExpression lambda, params Expression[] toReplace)
{
if (lambda.Parameters.Count != toReplace.Length)
throw new InvalidOperationException();
return new ExpressionReplacer(Enumerable.Range(0, lambda.Parameters.Count)
.ToDictionary(i => (Expression) lambda.Parameters[i], i => toReplace[i])).Visit(lambda.Body);
}
}
Strongly recommend this extension for Expression Tree visualization and debugging: https://marketplace.visualstudio.com/items?itemName=vs-publisher-1232914.ReadableExpressionsVisualizers
Hello i try to generate a single Func from a list combined by or.
var funcs = new List<Func<User, bool>>()
{
(u) => u.Id.Equals(entityToFind.Id),
(u) => u.UserName == entityToFind.UserName,
(u) => u.Email == entityToFind.Email
};
//TODO: Some magic that funs is euqaly to that:
Func<User, bool> func = (u) => u.Id.Equals(entityToFind.Id) || u.UserName == entityToFind.UserName || u.Email == entityToFind.Email;
I also tried it with Expressions, like that:
private Dictionary<string, Expression<Func<User, bool>>> private Dictionary<string, Expression<Func<User, bool>>> test(User entityToFind)
{
return new Dictionary<string, Expression<Func<User, bool>>>() {
{"Id", (u) => u.Id.Equals(entityToFind.Id) },
{"Name", (u) => u.UserName == entityToFind.UserName },
{"Email", (u) => u.Email == entityToFind.Email }
};
}
public static Expression<Func<T, bool>> ToOrExpression<T>(this Dictionary<string, Expression<Func<T, bool>>> dict)
{
var expressions = dict.Values.ToList();
if (!expressions.Any())
{
return t => true;
}
var delegateType = typeof(Func<T, bool>)
.GetGenericTypeDefinition()
.MakeGenericType(new[]
{
typeof(T),
typeof(bool)
}
);
var tfd = Expression.OrElse(expressions[0], expressions[1]);
var combined = expressions
.Cast<Expression>()
.Aggregate( (e1, e2) => Expression.OrElse(e1, e2) );
return (Expression<Func<T, bool>>)Expression.Lambda(delegateType, combined);
}
test(entityToFind).ToOrExpression();
But there i will get the following error:
The binary operator OrElse is not defined for the types 'System.Func2[Models.User,System.Boolean]' and
'System.Func2[Models.User,System.Boolean]'
While you could create a wrapper method to combine a bunch of Funcs, because you are using Entity Framework, that would cause the entire dataset to be downloaded into memory and the search done locally. What you should be using is Expression<Func<T, bool>> instead.
Fortunately Marc Gravell has already written a handy bit of code to combine expressions. Your question is strictly a dupliucate because you want to combine more than 2 together, but that is quite easy with a little Linq. So, lets start with your expressions first, the code barely changes:
var expressions = new List<Expression<Func<User, bool>>>()
{
(u) => u.Id.Equals(entityToFind.Id),
(u) => u.UserName == entityToFind.UserName,
(u) => u.Email == entityToFind.Email
};
Now using Marc's code and modifying it to be or instead of and:
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> OrElse<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var parameter = Expression.Parameter(typeof(T));
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], parameter);
var left = leftVisitor.Visit(expr1.Body);
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], parameter);
var right = rightVisitor.Visit(expr2.Body);
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(left, right), parameter);
}
private class ReplaceExpressionVisitor
: ExpressionVisitor
{
private readonly Expression _oldValue;
private readonly Expression _newValue;
public ReplaceExpressionVisitor(Expression oldValue, Expression newValue)
{
_oldValue = oldValue;
_newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == _oldValue)
return _newValue;
return base.Visit(node);
}
}
}
No you combine your expressions with the Linq Aggregate method:
var combinedExpression = expressions.Aggregate((x, y) => x.OrElse(y));
And use it something like this:
var result = db.Things.Where(combinedExpression);
I have a generic method that filters a list of entities, filtering is done by generating lambda expressions:
protected object initFilters<TEntity>(string targetEntity, List<SearchItem> searchItems, int page_size = 20, int offset = 0 , ExpressionSortCriteria<TEntity, string> SortCriteria)
{
var SearchQuery = new SearchQuery<TEntity>();
if (searchItems != null)
{
var predicat = ExpressionBuilder.GetExpression<TEntity>(searchItems).Compile();
SearchQuery.AddFilter(predicat);
}
//SearchQuery.AddSortCriteria(new ExpressionSortCriteria<Profile, string> { SortExpression = (profile => profile.Libelle), Direction = SortDirection.Ascending });
SearchQuery.Skip = offset;
SearchQuery.Take = page_size;
return (object)SearchQuery;
}
My SearchQuery contains criteria that will be applied to the list.
My predicate is build by a method in this way :
public static Expression<Func<T, bool>> GetExpression<T>(IList<SearchItem> filters)
{
some code ...
return Expression.Lambda<Func<T, bool>>(exp, param);
}
My AddFilter (in the searchQuery) is like this :
public void AddFilter(Expression<Func<TEntity, Boolean>> filter)
{
Filters.Add(filter);
}
I have a problem with this line :
SearchQuery.AddFilter(predicat);
Error:
cannot convert from System.Func to System.Linq.Expressions.Expression>
As you see , my two methods are using
Expression<Func<T, bool>>
Any idea please how to solve this ?
in your comment, you say you make
var SearchQuery = new SearchQuery<TEntity>();
if (searchItems != null) {
var predicat = ExpressionBuilder.GetExpression<TEntity>(searchItems).Compile();
SearchQuery.AddFilter(predicat);
}
of course, if your compile your Expression, it's no more an Expression<Func<T, bool>> but a Func<T, bool>
and AddFilter takes an Expression<Func<T, bool>> as argument.
So... remove the Compile()
I use this predicate with EF and lamdba expression :
public class PredicateBuilder
{
public static Expression<Func<T, bool>> True<T>() { return f => true; }
public static Expression<Func<T, bool>> False<T>() { return f => false; }
}
public static class ExpressionExtensions
{
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.And);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
return first.Compose(second, Expression.Or);
}
}
Now I just want to dynamically build this query :
Query(ufe => (ufe.FilmEtat.filmetat_code == etatString && ufe.user_id == 2) || (ufe.FilmEtat.filmetat_code == etatString && ufe.user_id == 11)).ToList();
I already tried :
var predicate = PredicateBuilder.True<UtilisateurFilmEtat>();
int i = 0;
foreach (int utilisateurId in listUtilisateurId)
{
if (i == 0)
predicate = ufe => (ufe.FilmEtat.filmetat_code == etatString && ufe.user_id == utilisateurId);
else
predicate.Or(ufe => ufe.FilmEtat.filmetat_code == etatString && ufe.user_id == utilisateurId);
i++;
}
The query is working but not return the good results...
I am becoming crazy :(
Need your help.
Thank you
Your question seems very similar to this one: Linq to SQL how to do "where [column] in (list of values)", although not an exact duplicate.
I can see that you're trying to dynamically build a query by combining other querys with ||, which is what you'd want to do if you only had a comparison operator...
Instead, how about something like this: ufe => listUtilisateurId.Contains(ufe.user_id)
I am using Expression.And and Expression.Or to build dynamic linq queries. When the property/field being queried is a string, and the string contains spaces I would like to tokenize the string on the spaces and create an "And'd" sub query on the tokens.
Here is what I mean in a non generic fashion
var tokens = Code.Split(new []{" "}, StringSplitOptions.RemoveEmptyEntries);
var index = 0;
var firstToken = tokens[index ++];
Expression<Func<Entity, bool>> subQuery =
entity => entity.Code.Contains(firstToken);
for (; index < tokens.Length; index ++)
{
var tempToken = tokens[index];
subQuery = subQuery.And(entity => entity.Code.Contains(tempToken));
}
query = query.Or(subQuery);
What I'd like to do is find a way of writing a method which is generic enough to just call for example:
PredicateBuilder.BuildTokenizedStringQuery<Entity>(
tokens, entity => entity.Code);
and I end up with the same result. The following is where I'm at but I can't use the Func stringProp accessor in and Expression. I have to somehow combine an accessor expression (of the string property) with an invocation expression (that invokes string.Contains)
private Expression<Func<T, bool>> BuildTokenizedStringQuery<T>(string[] tokens,
Func<T, string> stringProp)
{
var index = 0;
var firstToken = tokens[index++];
Expression<Func<T, bool>> subQuery = entity =>
stringProp(entity).Contains(firstToken);
for (; index < tokens.Length; index++)
{
var tempToken = tokens[index];
subQuery = subQuery.And(
entity => stringProp(entity).Contains(tempToken));
}
return subQuery;
}
I'd also be interested to hear if this all looks like a bad idea.
Here's what I use to do this:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Linq.Expressions;
using System.Collections.ObjectModel;
namespace MyLibrary.Extensions
{
/// <summary>Defines extension methods for building and working with Expressions.</summary>
public static class ExpressionExtensions
{
/// <summary>Ands the Expressions.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expressions">The Expression(s) to and.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> And<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
{
if (expressions.IsNullOrEmpty())
return null;
Expression<Func<T, bool>> finalExpression = expressions.First();
foreach (Expression<Func<T, bool>> e in expressions.Skip(1))
finalExpression = finalExpression.And(e);
return finalExpression;
}
/// <summary>Ors the Expressions.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expressions">The Expression(s) to or.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> Or<T>(this IEnumerable<Expression<Func<T, bool>>> expressions)
{
if (expressions.IsNullOrEmpty())
return null;
Expression<Func<T, bool>> finalExpression = expressions.First();
foreach (Expression<Func<T, bool>> e in expressions.Skip(1))
finalExpression = finalExpression.Or(e);
return finalExpression;
}
/// <summary>Ands the Expression with the provided Expression.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expression1">The left Expression to and.</param>
/// <param name="expression2">The right Expression to and.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
//Reuse the first expression's parameter
ParameterExpression param = expression1.Parameters.Single();
Expression left = expression1.Body;
Expression right = RebindParameter(expression2.Body, expression2.Parameters.Single(), param);
BinaryExpression body = Expression.AndAlso(left, right);
return Expression.Lambda<Func<T, bool>>(body, param);
}
/// <summary>Ors the Expression with the provided Expression.</summary>
/// <typeparam name="T">The target type of the Expression.</typeparam>
/// <param name="expression1">The left Expression to or.</param>
/// <param name="expression2">The right Expression to or.</param>
/// <returns>A new Expression.</returns>
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expression1, Expression<Func<T, bool>> expression2)
{
//Reuse the first expression's parameter
ParameterExpression param = expression1.Parameters.Single();
Expression left = expression1.Body;
Expression right = RebindParameter(expression2.Body, expression2.Parameters.Single(), param);
BinaryExpression body = Expression.OrElse(left, right);
return Expression.Lambda<Func<T, bool>>(body, param);
}
/// <summary>Updates the supplied expression using the appropriate parameter.</summary>
/// <param name="expression">The expression to update.</param>
/// <param name="oldParameter">The original parameter of the expression.</param>
/// <param name="newParameter">The target parameter of the expression.</param>
/// <returns>The updated expression.</returns>
private static Expression RebindParameter(Expression expression, ParameterExpression oldParameter, ParameterExpression newParameter)
{
if (expression == null)
return null;
switch (expression.NodeType)
{
case ExpressionType.Parameter:
{
ParameterExpression parameterExpression = (ParameterExpression)expression;
return (parameterExpression.Name == oldParameter.Name ? newParameter : parameterExpression);
}
case ExpressionType.MemberAccess:
{
MemberExpression memberExpression = (MemberExpression)expression;
return memberExpression.Update(RebindParameter(memberExpression.Expression, oldParameter, newParameter));
}
case ExpressionType.AndAlso:
case ExpressionType.OrElse:
case ExpressionType.Equal:
case ExpressionType.NotEqual:
case ExpressionType.LessThan:
case ExpressionType.LessThanOrEqual:
case ExpressionType.GreaterThan:
case ExpressionType.GreaterThanOrEqual:
{
BinaryExpression binaryExpression = (BinaryExpression)expression;
return binaryExpression.Update(RebindParameter(binaryExpression.Left, oldParameter, newParameter), binaryExpression.Conversion, RebindParameter(binaryExpression.Right, oldParameter, newParameter));
}
case ExpressionType.Call:
{
MethodCallExpression methodCallExpression = (MethodCallExpression)expression;
return methodCallExpression.Update(RebindParameter(methodCallExpression.Object, oldParameter, newParameter), methodCallExpression.Arguments.Select(arg => RebindParameter(arg, oldParameter, newParameter)));
}
case ExpressionType.Invoke:
{
InvocationExpression invocationExpression = (InvocationExpression)expression;
return invocationExpression.Update(RebindParameter(invocationExpression.Expression, oldParameter, newParameter), invocationExpression.Arguments.Select(arg => RebindParameter(arg, oldParameter, newParameter)));
}
default:
{
return expression;
}
}
}
public static Expression<Func<T, bool>> BuildContainsExpression<T, R>(Expression<Func<T, R>> valueSelector, IEnumerable<R> values)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
if (null == values)
throw new ArgumentNullException("values");
ParameterExpression parameterExpression = valueSelector.Parameters.Single();
IEnumerable<BinaryExpression> equalExpressions = null;
Expression aggregationExpression = null;
if (!values.IsNullOrEmpty())
return (e => false);
equalExpressions = values.Select(v => Expression.Equal(valueSelector.Body, Expression.Constant(v, typeof(R))));
aggregationExpression = equalExpressions.Aggregate<Expression>((accumulate, equal) => Expression.Or(accumulate, equal));
return Expression.Lambda<Func<T, bool>>(aggregationExpression, parameterExpression);
}
public static Expression<Func<T, bool>> BuildDoesNotContainExpression<T, R>(Expression<Func<T, R>> valueSelector, IEnumerable<R> values)
{
if (null == valueSelector)
throw new ArgumentNullException("valueSelector");
ParameterExpression parameterExpression = valueSelector.Parameters.Single();
IEnumerable<BinaryExpression> notEqualExpressions = null;
Expression aggregationExpression = null;
if (!values.IsNullOrEmpty())
return (e => false);
notEqualExpressions = values.Select(v => Expression.NotEqual(valueSelector.Body, Expression.Constant(v, typeof(R))));
aggregationExpression = notEqualExpressions.Aggregate<Expression>((accumulate, equal) => Expression.And(accumulate, equal));
return Expression.Lambda<Func<T, bool>>(aggregationExpression, parameterExpression);
}
}
}
Usage
string query = "kill mockingbird";
string[] tokens = query.Split(' ');
Expression<Func<Book, string>> inClause = BuildContainsExpression<Book, string>(o => o.Title, tokens);
using (LibraryDataContext dataContext = new LibraryDataContext())
{
List<Book> matchingBooks = dataContext.Books.Where(inClause).ToList();
}
Results
This will find all books whose title contains the words "kill" or "mockingbird."
The answer Josh provided is awesome and helped me to get exactly what I want. It however tests for equality of each token (it is also more generic as equality can be tested against any type) as opposed to a string.Contains test. Here is a solution that gives a string.Contains result:
public static Expression<Func<T, bool>>
BuildTokenizedStringQuery<T>(string[] tokens,
Expression<Func<T, string>> stringPropertyAccessor)
{
ParameterExpression parameterExpression = stringPropertyAccessor.Parameters
.Single();
var index = 0;
var firstToken = tokens[index ++];
Expression<Func<string, bool>> contains =
aString => aString.Contains(firstToken);
var invocation = Expression.Invoke(contains, stringPropertyAccessor.Body);
Expression<Func<T, bool>> expression = Expression
.Lambda<Func<T, bool>>(invocation, parameterExpression);
for (; index < tokens.Length; index++)
{
var tempToken = tokens[index];
contains = aString => aString.Contains(tempToken);
invocation = Expression.Invoke(contains, stringPropertyAccessor.Body);
expression = expression.And(Expression
.Lambda<Func<T, bool>>(invocation, parameterExpression));
}
return expression;
}