I'm using C# 2010 .NET 4.0 and I have a List<T> collection called returns that I need to build a dynamic LINQ query on.
I'm utilizing the Predicate Builder referenced here.
This works fine if I have 1 filter criteria, but if I have 2 or more, then, when the query.compile() is called, I get this error:
variable 'tmp' of type 'Check21Tools.IncomingReturn' referenced from
scope '', but it is not defined
Code:
Expression<Func<Check21Tools.IncomingReturn, bool>> query = null;
bool hasFilterItems = false;
if (filterArray != null)
{
foreach (string s in filterArray)
{
if (s == string.Empty)
{ break; }
else
{
hasFilterItems = true;
Int64 id = Int64.Parse(s);
query = query.Or(tmp => tmp.ID == id);
}
}
}
if (hasFilterItems)
{
returns = returns.Where(query.Compile()).CreateFromEnumerable
<Check21Tools.IncomingReturns, Check21Tools.IncomingReturn>();
}
Code:
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
if (expr1 == null) return expr2;
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, expr2.Body), expr1.Parameters);
}
[The OP has] stumbled across an updated version of the predicate builder that works on List<T> objects:
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
if (expr1 == null) return expr2;
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
Related
I have a search string that can be "sub1 sub2 sub3" and I want to write a proper Expression<Func<T, bool>> that can find "sub1", "sub2", "sub3" and "sub1 sub2 sub3" in the x.Name
In the other hand I want to modify x.Name.ToLower().Contains(productParams.Search) for my purpose.
Now I can search the term "sub1 sub2 sub3".
However, I want to search for sub-strings as well.
my expectation for the search is: "sub1" || "sub2" || "sub3" || "sub1 sub2 sub3"
productParams.Search = "sub1 sub2 sub3"
How can do it?
public class ProductsSpecification : BaseSpecifcation<Product>
{
public ProductsSpecification(ProductSpecParams productParams) : base(x =>
(string.IsNullOrEmpty(productParams.Search) ||
x.Name.ToLower().Contains(productParams.Search)) &&
(!productParams.BrandId.HasValue || x.ProductBrandId == productParams.BrandId))
}
BaseSpecifcation:
public class BaseSpecifcation<T> : ISpecification<T>
{
public BaseSpecifcation(Expression<Func<T, bool>> criteria)
{
Criteria = criteria;
}
public Expression<Func<T, bool>> Criteria { get; }
}
First of all, I created a helper class for generating predicates:
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 invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>> (Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1, Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>> (Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
I created a private method in ProductsSpecification and used my class helper:
private Expression<Func<Product, bool>> CreateProductFilter(ProductSpecParams productParams)
{
Expression<Func<Product, bool>> pr = PredicateBuilder.True<Product>(); // pr.Body.ToString() is "True"
if (!string.IsNullOrEmpty(productParams.Search) && !string.IsNullOrEmpty(productParams.Search.Trim()))
{
var searchValue = productParams.Search.Trim().ToLower();
pr = pr.And(a => a.Name.ToLower().Contains(searchValue));
foreach (var term in productParams.Search.ToLower().Split(' '))
{
string temp = term.Trim();
pr = pr.Or(a => a.Name.ToLower().Contains(temp));
}
}
if (productParams.BrandId.HasValue)
{
pr = pr.And(p => p.ProductBrandId == productParams.BrandId);
}
if (pr.Body.ToString() == "True")
{
return null;
}
return pr;
}
I modified the construcor of ProductsSpecification:
public ProductsSpecification(ProductSpecParams productParams) : base()
{
Criteria = CreateProductFilter(productParams);
// rest of the code
}
Now, the filter works well!
Maximize Client-Size UX in Azure Endpoint access via HTTP Requests by translating Query Parameters into an EF Core Predicate Expression.
using System;
using System.Linq;
using System.Reflection;
using System.ComponentModel;
using System.Linq.Expressions;
using System.Collections.Generic;
using Microsoft.AspNetCore.Http;
namespace EMWS_Projects.Services
{
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 invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
public static class QueryToExpressionConverter
{
public static Expression<Func<T, bool>> GetExpressionFromIQueryCollection<T>(IQueryCollection queryCollection)
{
Expression<Func<T, bool>> predicate = PredicateBuilder.True<T>();
foreach (KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues> entry in queryCollection)
{
predicate = predicate.And(GetExpressionFromEntry<T>(entry));
}
return predicate;
}
private static Expression<Func<T, bool>> GetExpressionFromEntry<T>(KeyValuePair<string, Microsoft.Extensions.Primitives.StringValues> entry)
{
PropertyInfo property = typeof(T).GetProperty(entry.Key.Trim(), BindingFlags.IgnoreCase | BindingFlags.Public | BindingFlags.Instance);
if (property == null) throw new Exception("Could not derive property from query parameter dictionary key.");
TypeConverter converter = TypeDescriptor.GetConverter(property.PropertyType);
List<string> values = entry.Value.ToList();
if (values == null || values.Count == 0) throw new Exception("Could not parse query parameter's value using the given property.");
Expression<Func<T, bool>> predicate = PredicateBuilder.False<T>();
foreach(string value in values)
{
predicate = predicate.Or((T subject) => typeof(T).GetProperty(entry.Key).GetValue(subject).ToString() == value);
}
return predicate;
}
}
}
I've a list of expressions:
List<Expression<Func<Domain.FollowUpActivity, bool>>> fuaExpressions = null;
I need to join all of them on a single OR expression:
I've tried this:
Expression body = Expression.Constant(false);
foreach (var orExpression in orExpressions)
foreach (Expression orExp in orExpression.Expressions)
body = Expression.Or(body, orExp);
It throws me an InvalidOperationException due to boolean operator is not defined between Expression<Boolean> and Expression<Func<T, bool>>
Some help please?
From a great book by Albahari brothers:
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 invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
Use it like this:
List<Expression<Func<Domain.FollowUpActivity, bool>>> fuaExpressions = null;
Expression<Func<Domain.FollowUpActivity, bool>> result = PredicateBuilder.False<Domain.FollowUpActivity>();
foreach (var exp in fuaExpressions)
{
result = PredicateBuilder.Or(result, exp);
}
in MongoDb I can pass a predicate to an queryable instance for example
DataBase.GetCollection<BsonDocument>("entity")
.AsQueryable<Entity>()
.Where(item=>item.id ==5);
But now I have function like this
IEnumerbale QueryData(Predicate<Entity> condition)
{
this.DataBase.GetCollection<BsonDocument>("entity")
.AsQueryable<Entity>()
.Where(item=> condition(item));
}
but this does not work and tells me:
Unsupported where clause: .
Is this as designed?
is there any workaround ? Am I doing something wrong?
You're not even passing an expression. Your condition is a totally opaque function to MongoDB.
You need to pass in an Expression<Func<Entity,bool>> and call Where like this:
Where(condition)
The Where clause must be translated to a MongoDB query that is sent to the server. When you pass in an arbitrary Predicate like that the LINQ layer has no idea what to translate it to. So that type of open-ended Where clause can't be supported.
Pass Lambda expression as parameter to filter data from mongodb collection.
Your data filter function could be
public IEnumerable<BsonDocument> FindAllWithPredicate(Func<BsonDocument, bool> condition)
{
return Collection.AsQueryable().Where(condition).ToArray();
}
Predicatebuilder.cs
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 invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke(expr2, expr1.Parameters.Cast<Expression>());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso(expr1.Body, invokedExpr), expr1.Parameters);
}
}
Generate lambda expression by predicate builder
public static class PredicateBuilderStore
{
public static Func<BsonDocument, bool> GetPredicateForBsonDocument(Entity entity)
{
var predicate = PredicateBuilder.True<BsonDocument>();
predicate = predicate.And(d => d.GetElement({key}).Value == CompareWithValue);
return predicate.Compile();
}
}
if you want to query just all items, you could do it as:
return this.collection.Find(_=>true).ToArray();
I have an application and I'm trying to implement DDD concepts. I have my repository class with some method to list entities. I would like to know how can I do a query with QueryOver to filter separating with AND operator, when the parameter is filled, sample
public IEnumerable<Product> FindProducts(string name, decimal? price, DateTime? validDate, int? stock, int? idSupplier)
{
var query = Session.QueryOver<Product>().OrderBy(x => x.Name).Asc;
if (!string.IsNullOrEmpty(name))
// add where condition for name parameter
if (price.HasValue)
// add 'AND' where condition for price parameter
if (validDate.HasValue)
// add 'AND' where condition for validDate parameter
if (idSupplier.HasValue)
// add 'AND' where condition for idSupplier parameter
// other possible conditions
return query.List();
}
Is there any way to do that before I use HQL string query? hehehe
Thank you!
Here, use PredicateBuilder:
How To:
IQueryable<Product> SearchProducts (params string[] keywords)
{
var predicate = PredicateBuilder.False<Product>();
foreach (string keyword in keywords)
{
string temp = keyword;
predicate = predicate.Or (p => p.Description.Contains (temp));
}
return dataContext.Products.Where (predicate);
}
PredicateBuilder Source:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Collections.Generic;
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 invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.OrElse (expr1.Body, invokedExpr), expr1.Parameters);
}
public static Expression<Func<T, bool>> And<T> (this Expression<Func<T, bool>> expr1,
Expression<Func<T, bool>> expr2)
{
var invokedExpr = Expression.Invoke (expr2, expr1.Parameters.Cast<Expression> ());
return Expression.Lambda<Func<T, bool>>
(Expression.AndAlso (expr1.Body, invokedExpr), expr1.Parameters);
}
}
For more information on PredicateBuilder and LinqKit go here: http://www.albahari.com/nutshell/linqkit.aspx