Bad result using query generated automatically with predicate and OR condition - linq

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)

Related

EF Core, append to predicate builder in Any condition

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

Ravendb Session.Query<T>() method does not return any result when sending Expression<Func<T, bool>> to Where() Extension method

I've got a problem with Ravendb session.Query.Where().
When I use it like this it works perfectly and returns all of UserIdentitiy with User name 'John' :
users = session.Query<UserIdentity>().Where(x => x.UserName == "John").ToList();
But when I try to send the Expression as a variable to Where method it fails silently and returns nothing.
Expression<Func<UserIdentity, bool>> whereClause = x => x.UserName == "John";
using (var session = _store.OpenSession())
{
users = session.Query<UserIdentity>().Where(whereClause).ToList();
}
What's wrong?
Try the following code:
public static class ExpressionTreesExtension
{
public static Expression<Func<T, bool>> EqualExpression<T>(string columnName,object value)
{
ParameterExpression parameterType = Expression.Parameter(typeof(T), "object");
MemberExpression typeColumn = Expression.Property(parameterType, columnName);
ConstantExpression constant = Expression.Constant(value, typeof(string));
BinaryExpression binaryExpression = Expression.Equal(typeColumn, constant);
return Expression.Lambda<Func<T, bool>>(binaryExpression, parameterType);
}
}
Code Usage:
Expression<Func<UserIdentity, bool>> whereClause = ExpressionTreesExtension.EqualExpression<UserIdentity>("UserName","John");

Generating lambda expression - Type conversion Error

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()

How to use Expression for Linq union and intersect?

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.

If statements within a Linq where clause

Struggling a bit today.
I have the following method that returns a list of products..lovely.
public static List<tblWeight> GetProductInfo(string memberid, string locationid, string basematerial, string source)
{
MyEntities getproductinfo = new MyEntities ();
return (from p in getproductinfo .tblWeights
where p.MemberId == memberid &&
p.LocationId == locationid &&
p.BaseMaterialName == basematerial &&
p.WeightStatus == source
select p)
.ToList();
Where basematerial & source are drop down lists.
How do I go about incorporating a few IF statements into the where clause?
For example, if the basematerial ddl is not touched but an item in the source ddl is selected, the result would return everything associated with basematerial but filtered by the selected source.
Does that even make sense?!
I'm not even sure I am taking the correct approach - please forgive my ignorance.
you can add them to your query on need:
var r = (from p in getproductinfo .tblWeights
where p.MemberId == memberid &&
p.LocationId == locationid &&
p.WeightStatus == source
select p)
if (!String.IsNullOrEmpty(basematrial))
r = r.Where(p => p.BaseMaterialName == basematerial);
return r.ToList();
Consider implementing these extension methods named WhereIf.
You pass it two parameters: a statement evaluated to a boolean, and a lambda function. If the bool statement evaluates to true, the lambda is added.
WhereIf on ExtensionMethod.net
Your query could look like:
return getproductinfo.tblWeights
.Where(w=> w.MemberId == memberid &&
w.LocationId == locationid)
.WhereIf(!string.IsNullOrEmpty(basematerial), w=>w.BaseMaterialName == basematerial)
.WhereIf(!string.IsNullOrEmpty(source), w=>w.WeightStatus == source)
.ToList();
Here they are, for both IEnumerable and IQueryable. This allows you to use .WhereIf() in LINQ To SQL, Entity Framework, Lists, Arrays, and anything else that implements those 2 interfaces.
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, int, bool> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}
public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Func<TSource, int, bool> predicate)
{
if (condition)
return source.Where(predicate);
else
return source;
}

Resources