I need to reduce an expression
Expression<Func<TQueryResult, TParam, bool>>
to
Expression<Func<TQueryResult, bool>>
by injecting TParam value as a constant into the expression.
Concrete example:
protected IQueryable<TQueryResult> AddQueryFilter<TQueryResult, TParam>(IQueryable<TQueryResult> query, Expression<Func<TQueryResult, TParam, bool>> exp, TParam param)
{
object obj = param;
if (obj is string)
{
if (!string.IsNullOrWhiteSpace((string) obj))
{
var reducedExp = new Expression<Func<TQueryResult, bool>>()
// ...
// the magic that I need to inject param value
//..
return query.Where(reducedExp);
}
}
else if (obj is DateTime)
{
//... return query.Where(reducedExp);
}
else
throw new InvalidOperationException("Param type not supported");
return query;
}
//usage
var qr = Manager.Invoices.Query;
qr = AddQueryFilter(qr, (invoice, value) => value == invoice.Number, numberEdit.Text);
qr = AddQueryFilter(qr, (invoice, value) => value == invoice.Date, dateEdit.Date);
qr = AddQueryFilter(qr, (invoice, value) => invoice.Description.Contains(value), descEdit.Text);
Try:
protected static IQueryable<TQueryResult> AddQueryFilter<TQueryResult, TParam>(
IQueryable<TQueryResult> query, Expression<Func<TQueryResult, TParam, bool>> exp, TParam param)
{
var rewriter = new ExpressionRewriter();
rewriter.Subst(exp.Parameters[1], Expression.Constant(param, typeof(TParam)));
var body = rewriter.Apply(exp.Body);
var lambda = Expression.Lambda<Func<TQueryResult, bool>>(body, exp.Parameters[0]);
return query.Where(lambda);
}
using ExpressionRewriter from this answer.
I hope somebody still can be searching for this topic, as me, in fact, so I'd like to suggest the following possibility.
Since .NET 4.0 has been released, you can use built-in expression tree visitors.
Here's an exapmple, which implements required functionality:
private class ExpressionConstantInjector<T, TConstant> : ExpressionVisitor
{
private readonly TConstant toInject;
private readonly ParameterExpression targetParam;
public EntityExpressionListInjector(TConstant toInject)
{
this.toInject = toInject;
targetParam = Expression.Parameter(typeof(T), "a");
}
public Expression<Func<T, bool>> Inject(Expression<Func<T, TConstant, bool>> source)
{
return (Expression<Func<T, bool>>) VisitLambda(source);
}
protected override Expression VisitLambda<T1>(Expression<T1> node)
{
return Expression.Lambda(Visit(node.Body), targetParam);
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node.Type == typeof (TConstant))
return Expression.Constant(toInject);
return targetParam;
}
}
Usage:
Expression<Func<Entity, List<int>, bool>> expression = (e, ids) => ids.Contains(e.Id);
var filterExpression
= new ExpressionConstantInjector<Entity, List<int>>(new List<int>{1, 2, 3})
.Inject(expression);
// filterExpression is like a => (1, 2, 3).Contains(a.Id)
// filterExpression can be passed to EF IQueryables.
This solution is very local, not really reusable, but quiet simple (nah).
To be honest, [].Contains(id) is the only case I've tested. But I think it works.
Related
public IEnumerable<Table1> GetMatchingTable1(string param, double[] Thicknesses)
{
return DBContext.Table1.Where(c => c.Field1 == param
&& Thicknesses.Any(Thickness => Thickness >= c.MinThickness && Thickness <= c.MaxThickness))
.ToList();
}
Above query return the following exception. "Some part of your SQL statement is nested too deeply. Rewrite the query or break it up into smaller queries."
So far, all my research on the web for this error pointed toward replacing "ANY" with "CONTAINS". Here is one site where they fix the problem using this solution : http://blog.hompus.nl/2010/08/26/joining-an-iqueryable-with-an-ienumerable/
But in my case, "CONTAINS" doesn't seem usable since I check a RANGE with Min and Max.
How should this query be written to have a proper SQL Statement generated by LinqToEntity?
Thanks
You could try to build the query dynamically:
public IEnumerable<Table1> GetAllCoilLengthSettingsWithChilds(string param, double[] Thicknesses)
{
// Base query
var query = LinqKit.Extensions.AsExpandable(DBContext.Table1.Where(c => c.Field1 == param));
// All the various || between the Thickness ranges
var predicate = LinqKit.PredicateBuilder.False<Table1>();
foreach (double th in Thicknesses)
{
// Don't want a closure around th
double th2 = th;
predicate = predicate.Or(c => th2 >= c.MinThickness && th2 <= c.MaxThickness);
}
// This is implicitly in && with the other Where
query = query.Where(predicate);
return query.ToList();
}
The PredicateBuilder helps you build an || query. Take it from the LinqKit (source available)
I've tested it with 1000 parameters (but they where DateTime, and I didn't have other query pieces), and it seems to work. Note that the program uses another extension of LinqPad, AsExpandable, used to make the PredicateBuilder "trick" work. Note that I'm using EF 6.1.3, so your mileage may vary.
If you don't want to use LinqKit, I'm appending my version of PredicateBuilder. It doesn't require the use of AsExpandable(), but its syntax is slightly different:
public class PredicateBuilder<T>
{
// We share a single parameter for all the PredicatBuilder<T>
// istances. This isn't a proble, because Expressions are immutable
protected static readonly ParameterExpression Parameter = Expression.Parameter(typeof(T), "x");
protected Expression Current { get; set; }
// Returns an empty PredicateBuilder that, if used, is true
public PredicateBuilder()
{
}
// Use it like this: .Where(predicate) or .Any(predicate) or
// .First(predicate) or...
public static implicit operator Expression<Func<T, bool>>(PredicateBuilder<T> predicate)
{
if (object.ReferenceEquals(predicate, null))
{
return null;
}
// Handling of empty PredicateBuilder
Expression current = predicate.Current ?? Expression.Constant(true);
Expression<Func<T, bool>> lambda = Expression.Lambda<Func<T, bool>>(current, Parameter);
return lambda;
}
public static implicit operator PredicateBuilder<T>(Expression<Func<T, bool>> expression)
{
var predicate = new PredicateBuilder<T>();
if (expression != null)
{
// Equivalent to predicate.Or(expression)
predicate.And(expression);
}
return predicate;
}
public void And(Expression<Func<T, bool>> expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
this.Current = this.Current != null ? Expression.AndAlso(this.Current, expression2) : expression2;
}
public void Or(Expression<Func<T, bool>> expression)
{
if (expression == null)
{
throw new ArgumentNullException("expression");
}
var expression2 = new ParameterConverter(expression.Parameters[0], Parameter).Visit(expression.Body);
this.Current = this.Current != null ? Expression.OrElse(this.Current, expression2) : expression2;
}
public override string ToString()
{
// We reuse the .ToString() of Expression<Func<T, bool>>
// Implicit cast here :-)
Expression<Func<T, bool>> expression = this;
return expression.ToString();
}
// Small ExpressionVisitor that replaces the ParameterExpression of
// an Expression with another ParameterExpression (to make two
// Expressions "compatible")
protected class ParameterConverter : ExpressionVisitor
{
public readonly ParameterExpression From;
public readonly ParameterExpression To;
public ParameterConverter(ParameterExpression from, ParameterExpression to)
{
this.From = from;
this.To = to;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (node == this.From)
{
node = this.To;
}
return base.VisitParameter(node);
}
}
}
public static class PredicateBuilder
{
// The value of source isn't really necessary/interesting. Its type
// is :-) By passing a query you are building to Create, the compiler
// will give to Create the the of the object returned from the query
// Use it like:
// var predicate = PredicateBuilder.Create<MyType>();
// or
// var predicate = PredicateBuilder.Create(query);
public static PredicateBuilder<T> Create<T>(IEnumerable<T> source = null)
{
return new PredicateBuilder<T>();
}
// Useful if you want to start with a query:
// var predicate = PredicateBuilder.Create<MyType>(x => x.ID != 0);
// Note that if expression == null, then a new PredicateBuilder<T>()
// will be returned (that by default is "true")
public static PredicateBuilder<T> Create<T>(Expression<Func<T, bool>> expression)
{
// Implicit cast to PredicateBuilder<T>
return expression;
}
}
Use it like:
var predicate = PredicateBuilder.Create(query);
and then everything is the same (but remove the LinqKit.Extensions.AsExpandable part)
Is there a way to convert the property selector Expression<Func<T, TProperty>> to Expression<Func<object, object>> and vice versa? I already know how to convert to Expression<Func<T, object>> using...
Expression<Func<T, TProperty>> oldExp;
Expression.Lambda<Func<T, object>>(Expression.Convert(oldExp.Body, typeof(object)), oldExp.Parameters);
... but I need to effectively cast both, the argument and the result of the function, not just e.g replace them with a ExpressionVisitor because they need to be casted back later.
You were correct that you need to use an ExpressionVisitor and ExpressionConvert.
Here are the two methods that you asked for (as well as some support methods):
public Expression<Func<object, object>> ConvertToObject<TParm, TReturn>(Expression<Func<TParm, TReturn>> input)
{
var parm = Expression.Parameter(typeof(object));
var castParm = Expression.Convert(parm, typeof(TParm));
var body = ReplaceExpression(input.Body, input.Parameters[0], castParm);
body = Expression.Convert(body, typeof(object));
return Expression.Lambda<Func<object, object>>(body, parm);
}
public Expression<Func<TParm, TReturn>> ConvertBack<TParm, TReturn>(Expression<Func<object, object>> input)
{
var parm = Expression.Parameter(typeof(TParm));
var castParm = Expression.Convert(parm, typeof(object));
var body = ReplaceExpression(input.Body, input.Parameters[0], castParm);
body = Expression.Convert(body, typeof(TReturn));
return Expression.Lambda<Func<TParm, TReturn>>(body, parm);
}
Expression ReplaceExpression(Expression body, Expression source, Expression dest)
{
var replacer = new ExpressionReplacer(source, dest);
return replacer.Visit(body);
}
public class ExpressionReplacer : ExpressionVisitor
{
Expression _source;
Expression _dest;
public ExpressionReplacer(Expression source, Expression dest)
{
_source = source;
_dest = dest;
}
public override Expression Visit(Expression node)
{
if (node == _source)
return _dest;
return base.Visit(node);
}
}
Sample usage:
Expression<Func<Customer, string>> expression = c => c.Name;
var convertedExpression = ConvertToObject<Customer, string>(expression);
var backExpression = ConvertBack<Customer, string>(convertedExpression);
Of course we can replace the original two methods with one method with more type parameters:
public Expression<Func<TTargetParm, TTargetReturn>> ConvertGeneric<TParm, TReturn, TTargetParm, TTargetReturn>(Expression<Func<TParm, TReturn>> input)
{
var parm = Expression.Parameter(typeof(TTargetParm));
var castParm = Expression.Convert(parm, typeof(TParm));
var body = ReplaceExpression(input.Body, input.Parameters[0], castParm);
body = Expression.Convert(body, typeof(TTargetReturn));
return Expression.Lambda<Func<TTargetParm, TTargetReturn>>(body, parm);
}
Sample usage:
Expression<Func<Customer, string>> expression = c => c.Name;
var convertedExpression = ConvertGeneric<Customer, string, object, object>(expression);
var backExpression = ConvertGeneric<object, object, Customer, string>(convertedExpression);
I've looked into many generic linq filtering questions and their answers here in SO but none of them satisfy my needs so I thought I should create a question.
I've created many of what I call "filter provider" classes, one for each entity class in my model, to provide a simplistic search for my application. I didn't want to go into more advanced solutions like Lucene.Net because a basic filtering with matching score would suffice.
Inside each one of these provider classes there are multiple methods that will receive the filtering terms and query specific properties, returning a score for each match based on the relevance of the property. Most methods will filter multiple properties at once, but not all.
Here are two of these methods:
private IQueryable<Retailer> MatchHighRelevanceFields(string searchTerm, IQueryable<Retailer> retailers)
{
var results = retailers.Where(r =>
(r.CompanyName != null && r.CompanyName.ToUpper().Contains(searchTerm))
|| (r.TradingName != null && r.TradingName.ToUpper().Contains(searchTerm))
);
return results;
}
private IQueryable<Retailer> MatchMediumRelevanceFields(string searchTerm, IQueryable<Retailer> retailers)
{
var results = retailers.Where(r =>
(r.Address.Street != null && r.Address.Street.ToUpper().Contains(searchTerm))
|| (r.Address.Complement != null && r.Address.Complement.ToUpper().Contains(searchTerm))
);
return results;
}
These methods are replicated ad nauseum throughout each provider class and I hope I could replace them for a single method that would receive the properties to be included in the query.
Something like:
public static IQueryable<T> Match<T>(string searchTerm, IQueryable<T> data, Expression<Func<T, string>> filterProperties)
{
var results = **build the query for each property in filterProperties**
return results;
}
But I really can't figure it out. I tried using reflection but it only worked with Linq to Objects and I need a solution for Linq to Entities.
So to solve this problem we need a few puzzle pieces first. The first puzzle piece is a method that can take an expression that computes a value, and then another expression that computes a new value taking the same type the first returns, and creates a new expression that represents the result of passing the result of the first function as the parameter to the second. This allows us to Compose expressions:
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);
}
This relies on the following tool to replace all instances of one expression with another:
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);
}
}
We'll also need a tool to help us OR two predicate expressions together:
public static 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 Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, secondBody), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(
this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var secondBody = expr2.Body.Replace(
expr2.Parameters[0], expr1.Parameters[0]);
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, secondBody), expr1.Parameters);
}
}
Now that we have this we can use Compose on each property selector to map it from the property results to whether or not that property value is non-null and contains the search term. We can then OR all of those predicates together to get a filter for your query:
public static IQueryable<T> Match<T>(
IQueryable<T> data,
string searchTerm,
IEnumerable<Expression<Func<T, string>>> filterProperties)
{
var predicates = filterProperties.Select(selector =>
selector.Compose(value =>
value != null && value.Contains(searchTerm)));
var filter = predicates.Aggregate(
PredicateBuilder.False<T>(),
(aggregate, next) => aggregate.Or(next));
return data.Where(filter);
}
You can do it with expression trees but it's not as simple as you might think.
public static IQueryable<T> Match<T>(this IQueryable<T> data, string searchTerm,
params Expression<Func<T, string>>[] filterProperties)
{
var parameter = Expression.Parameter(typeof (T), "source");
Expression body = null;
foreach (var prop in filterProperties)
{
// need to replace all the expressions with the one parameter (gist taken from Colin Meek blog see link on top of class)
//prop.body should be the member expression
var propValue =
prop.Body.ReplaceParameters(new Dictionary<ParameterExpression, ParameterExpression>()
{
{prop.Parameters[0], parameter}
});
// is null check
var isNull = Expression.NotEqual(propValue, Expression.Constant(null, typeof(string)));
// create a tuple so EF will parameterize the sql call
var searchTuple = Tuple.Create(searchTerm);
var matchTerm = Expression.Property(Expression.Constant(searchTuple), "Item1");
// call ToUpper
var toUpper = Expression.Call(propValue, "ToUpper", null);
// Call contains on the ToUpper
var contains = Expression.Call(toUpper, "Contains", null, matchTerm);
// And not null and contains
var and = Expression.AndAlso(isNull, contains);
// or in any additional properties
body = body == null ? and : Expression.OrElse(body, and);
}
if (body != null)
{
var where = Expression.Call(typeof (Queryable), "Where", new[] {typeof (T)}, data.Expression,
Expression.Lambda<Func<T, bool>>(body, parameter));
return data.Provider.CreateQuery<T>(where);
}
return data;
}
public static Expression ReplaceParameters(this Expression exp, IDictionary<ParameterExpression, ParameterExpression> map)
{
return new ParameterRebinder(map).Visit(exp);
}
Now you need to have a expressionvisitor to make all the expressions use one parameter
//http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
public class ParameterRebinder : ExpressionVisitor
{
private readonly IDictionary<ParameterExpression, ParameterExpression> _map;
public ParameterRebinder(IDictionary<ParameterExpression, ParameterExpression> map)
{
_map = map;
}
protected override Expression VisitParameter(ParameterExpression node)
{
if (_map.ContainsKey(node))
{
return _map[node];
}
return base.VisitParameter(node);
}
}
Would use it like
var matches = retailers.Match("7", r => r.Address.Street, x => x.Address.Complement).ToList();
Warning - I checked this with linq to objects using the AsQueryable but didn't run it against EF.
You can use Linq.Dynamic to build the query.
public static IQueryable<T> Match<T>(
string searchTerm,
IQueryable<T> data,
params Expression<Func<T, string>>[] filterProperties) where T : class
{
var predicates = new List<string>();
foreach (var prop in filterProperties)
{
var lambda = prop.ToString();
var columnName = lambda.Substring(lambda.IndexOf('.') + 1);
var predicate = string.Format(
"({0} != null && {0}.ToUpper().Contains(#0))", columnName);
predicates.Add(predicate);
}
var filter = string.Join("||", predicates);
var results = data.Where(filter, searchTerm);
return results;
}
Usage.
var retailers = Match(
"asd", db.Retailers, r => r.CompanyName, r => r.TradingName);
var retailers = Match(
"asd", db.Retailers, r => r.Address.Street, r => r.Address.Complement);
Limitation.
The filter can only accept basic expression.
r => r.Name
r => r.PropA.Name
r => r.PropA.PropB.Name
Try to use Expressions like those all
http://www.codeproject.com/Articles/493917/Dynamic-Querying-with-LINQ-to-Entities-and-Express
all
I try to build a dynamic linq query by expression tree, below is my code
Expression<Func<User, bool>> filter = c => c.isAdmin == false;
Expression<Func<User, bool>> filterForExistUser = c => (c.isfreezed == null ? false : c.isfreezed) != true;
Expression<Func<User, bool>> finalFilter = Expression.Lambda<Func<User, bool>>(Expression.AndAlso(filter.Body, filterForExistUser.Body), filter.Parameters);
IQueryable<User> myusers = db.Users.AsQueryable<User>();
MethodCallExpression whereCallExpression = Expression.Call(
typeof(Queryable),
"Where",
new Type[] { myusers.ElementType },
myusers.Expression,
finalFilter);
IQueryable<User> results = myusers.Provider.CreateQuery<User>(whereCallExpression);
foreach (User user in results)
Console.WriteLine(user.UserName);
But system report "unding parameter c" error, how I can fix it?
Thanks
Although the parameters in the filter and filterForExistUser expressions share the same name, they both are referring to different symbols from different scopes. Your new expression uses the parameter from the filter expression, but you left out the parameter for the filterForExistUser. You must rewrite the expression so the same single parameter is used throughout the overall expression.
Here's a general purpose method you can use to combine the predicates:
Expression<Func<TSource, bool>> CombinePredicates<TSource>(
Expression<Func<TSource, bool>> head,
params Expression<Func<TSource, bool>>[] tail)
{
var param = head.Parameters.Single();
var body = tail.Aggregate(
head.Body,
(result, expr) => Expression.AndAlso(result,
new SubstitutionVisitor
{
OldExpr = expr.Parameters.Single(),
NewExpr = param,
}.Visit(expr.Body)
)
);
return Expression.Lambda<Func<TSource, bool>>(body, param);
}
public class SubstitutionVisitor : ExpressionVisitor
{
public Expression OldExpr { get; set; }
public Expression NewExpr { get; set; }
public override Expression Visit(Expression node)
{
return (node == OldExpr) ? NewExpr : base.Visit(node);
}
}
The key is the SubstitutionVisitor which is used to substitute the "tail" paramters using the "head" parameter.
So your final filter becomes this:
Expression<Func<User, bool>> finalFilter =
CombinePredicates(filter, filterForExistUser);
So what I am trying to do is use expression trees to apply a predicate to each value in a collection (read map or list.All(predicate)). It appears that I am not getting the input parameter to the predicate bound to the value supplied by All, and I'm a little stuck. Here is the code (using linqpad) that I am working with::
public class SomeType
{
public IEnumerable<bool> Collection { get; set; }
}
void Main()
{
var list = new SomeType {
Collection = new List<bool> { true, true, true }
};
var functor = Compiler((SomeType t) => t.Collection, (bool x) => x);
functor(list).Dump();
}
MethodInfo FindMethod<TInput>(Type location, string name)
{
var handle = location
.GetMethods(BindingFlags.Static | BindingFlags.Public)
.Where(method => method.Name == name).First();
return handle.MakeGenericMethod(typeof(TInput));
}
Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Predicate<TProperty>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var expression = Expression.Call(query,
new Expression[] {
Expression.Invoke(selector, selector.Parameters),
Expression.Lambda<Func<TProperty, bool>>(predicate.Body,
Expression.Parameter(typeof(TProperty))),
});
return Expression.Lambda<Predicate<TObject>>(expression,
selector.Parameters).Compile();
}
Thanks and sorry if this was answered in another question (I looked for a while).
This does work, but I had to change the Predicate<TObject> to Func<TObject, bool>. If you want I can try to change it back.
static Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Func<TProperty, bool>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var expression = Expression.Call(
query,
Expression.Invoke(selector, selector.Parameters),
predicate);
return Expression
.Lambda<Predicate<TObject>>(expression, selector.Parameters)
.Compile();
}
5 minutes later... And if you really want to use Predicate<TObject>...
static Predicate<TObject> Compiler<TObject, TProperty>(
Expression<Func<TObject, IEnumerable<TProperty>>> selector,
Expression<Predicate<TProperty>> predicate)
{
var query = FindMethod<TProperty>(typeof(Enumerable), "All");
var predicateAsFunc = Expression.Lambda<Func<TProperty, bool>>(
predicate.Body,
predicate.Parameters);
var expression = Expression.Call(
query,
Expression.Invoke(selector, selector.Parameters),
predicateAsFunc);
return Expression
.Lambda<Predicate<TObject>>(expression, selector.Parameters)
.Compile();
}