I want to create dynamic query builder using LINQ to SQL
For that, I created my interface that add each dynamic condition in
List<Expression<Func<T,bool>>>
Interface looks like :
public interface IExpression<T>
{
IExpression<T> AddWhere(Expression<Func<T,bool>> whereCriteria);
}
Now I want to combine all expression in list and construct where clause with "and" condition and execute query.
I tried combining expression but not succeeded in that attempt.
Can anyone please help ? or please suggest any other alternative.
The easiest way is to use PredicateBuilder: http://www.albahari.com/nutshell/predicatebuilder.aspx
Basically, all you have to do is make use of this helper class:
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);
}
}
And then you can use it like so:
public static Expression<Func<Product, bool>> ContainsInDescription (
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 predicate;
}
(both the code and the example are taken from the link above, I just posted it here in case the link doesn't work sometime).
Your particular scenario is somewhat complicated by the fact that your interface doesn't use generics. Could you show a bit more of the relevant code, so that I can help tailor this solution better to your actual needs?
Related
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'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);
}
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
I have an array of
Expression<Func<MyClass,bool>>
However, I want to AND them all together to get just a single item of that type. How do I do this? Can I cast the result of Expression.And?
If you use the following extension method:
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);
}
From here: http://www.albahari.com/nutshell/predicatebuilder.aspx
Then you can just write this to fold them all down to one expression.
public Expression<Func<T, bool>> AggregateAnd(Expression<Func<T,bool>>[] input)
{
return input.Aggregate((l,r) => l.And(r));
}