Linq PredicateBuilder, grouping and operator precedence - linq

Here is an example of the problem:
var source = new LambdasTestEntity[] {
new LambdasTestEntity {Id = 1},
new LambdasTestEntity {Id = 2},
new LambdasTestEntity {Id = 3},
new LambdasTestEntity {Id = 4},
};
Expression<Func<LambdasTestEntity, bool>> expression1 = x => x.Id == 1;
Expression<Func<LambdasTestEntity, bool>> expression2 = x => x.Id == 3;
Expression<Func<LambdasTestEntity, bool>> expression3 = x => x.Id > 2;
// try to chain them together in a following rule
// Id == 1 || Id == 3 && Id > 2
// as && has higher precedence, we expect getting two entities
// with Id=1 and Id=3
// see how default LINQ works first
Expression<Func<LambdasTestEntity, bool>> expressionFull = x => x.Id == 1 || x.Id == 3 && x.Id > 2;
var filteredDefault = source.AsQueryable<LambdasTestEntity>()
.Where(expressionFull).ToList();
Assert.AreEqual(2, filteredDefault.Count); // <-this passes
// now create a chain with predicate builder
var totalLambda = expression1.Or(expression2).And(expression3);
var filteredChained = source.AsQueryable<LambdasTestEntity>()
.Where(totalLambda).ToList();
Assert.AreEqual(2, filteredChained.Count);
// <- this fails, because PredicateBuilder has regrouped the first expression,
// so it now looks like this: (Id == 1 || Id == 3) && Id > 2
When I look in Watches for both expressions, I see the following:
expressionFull as it is coming from Linq:
(x.Id == 1) OrElse ((x.Id == 3) AndAlso (x.Id > 2))
totalLambda for PredicateBuilder:
((x.Id == 1) OrElse Invoke(x => (x.Id == 3), x)) AndAlso Invoke(x => (x.Id > 2), x)
I find it is a bit unsafe to use the PredicateBuilder if it behaves differently from default Linq Expression builder.
Now some questions:
1) Why is Linq creating those groups? Even if I create an Or expression
x => x.Id == 1 || x.Id == 3 || x.Id > 2
I stil get the first two criteria grouped like this:
((x.Id == 1) OrElse (x.Id == 3)) OrElse (x.Id > 2)
Why it is not just
(x.Id == 1) OrElse (x.Id == 3) OrElse (x.Id > 2)
?
2) Why PredicateBuilder is adding those Invokes? I don't see Invokes in the default Linq expression result, so they seem useless...
3) Is there any other way to construct the expression "offline" and then pass to the default Linq Expression builder? Something like this:
ex = x => x.Id == 1;
ex = ex || x.Id == 3;
ex = ex && x.Id > 2;
and then Linq Expression builder then parses it and creates the same expression as it does for x => x.Id == 1 || x.Id == 3 && x.Id > 2 (giving && higher precedence)?
Or maybe I could tweak the PredicateBuilder to do the same?

Expanding on my comment above:
Because it has no concept of operator precedence here. You're
literally building the expression tree yourself, and "piping" the
results of one method to the next determines order. Thus, the order of
the resulting expression is going to be exactly what you specified.
The complete source for PredicateBuilder is posted here and shows just how simple it is. But it also shows you the source of your problem above. In case you don't want to visit Albahari's site, here's the complete source:
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);
}
The main thing to notice here is that it builds the expression one node at a time, then pipes this node as being the left expression (leaf) of the subsequent node. The Expression.Invoke call is simply to pipe the parameters from your existing node into the right leaf (the next expression), and the rest is pretty self explanatory.
Edit: I had to do something similar to this (but didn't use PredicateBuilder, built the trees myself using Expression calls). The main thing to keep in mind is that you just have to process the And/AndAlso nodes first, and then handle the Or/OrElse nodes, that way you build the tree with the proper precedence. Unfortunately, building ExpressionTrees by hand are very much a step-by-step process, so you have to make sure you break each of the steps down into the correct order to get the results you want/need.

Related

How to create programmatically single LINQ query w/ OR between Where() clauses (.Where(fn) OR .Where(fn) OR .Where(fn)) programatically

I'd like to know it is it possible to create programmatically single LINQ query (for EntityFramework 6) with N .Where() clauses, but with OR between these .Where() clauses.
Imagine IQueryable object defined like:
var query = dbContext.MyTable.Where(mt => mt.TimeStamp >= DateBegin);
What I need else is add N (unknown number) of Where clauses, but with OR condition between them.
Image list of some object:
List<MyObject> myObj =
new List<MyObject>({new MyObject {val = "a" }, new MyObject { val = "b"}}); //In real code there is more than 1 property.
then I'd like to add Where() clauses to query like:
myObj.ForEach(mo =>{
// THIS CREATES -AND- BETWEEN WHERE CLAUSES, BUT I NEED -OR-
query.Where(q=>q.MyValue == mo.val); // In real code there is more than 1 property to compare
});
I was thinking about .Union() beteween queries, but It could generate union between separated queries and it's not optimal I think.
Thanks!
Here's the solution: linq-to-entities-combining-predicates
Or course is necessary to use "latest" answer:
Copy/Paste class ParameterRebinder
Copy/Paste static class Utility
Usage:
Expression<Func<Car, bool>> theCarIsRed = c => c.Color == "Red";
Expression<Func<Car, bool>> theCarIsCheap = c => c.Price < 10.0;
Expression<Func<Car, bool>> theCarIsRedOrCheap = theCarIsRed.Or(theCarIsCheap);
var query = carQuery.Where(theCarIsRedOrCheap);
Because in my solution is N of expressions, I take first expression and then append other expressions in ForEach cycle.
var firstExpression = expressionList.First();
expressionList.Skip(1).ToList().ForEach(ex => { firstExpression = firstExpression.Or(ex); });

linq query with dynamic predicates in where clause joined by OR

you can easily create dynamic queries in c# if you add more restrictions to the current query.
var list = new List<Item>();
var q = list.AsQueryable();
q = q.Where(x => x.Size == 3);
q = q.Where(x => x.Color == "blue");
In this case, every new predicate is added performing an AND operation with the previous. The previous result is equivalent to:
q = list.Where(x => x.Size == 3 && x.Color == "blue");
Is it possible to achieve the same result but with OR instead of AND?
q = list.Where(x => x.Size == 3 || x.Color == "blue");
The idea is to have a variable number of expressions that are joined with OR operator.
Expected result would need to be written in some how similar to the following pseudo code:
var conditions = new List<Func<Item, bool>>();
And later iterate conditions to build something like:
foreach(var condition in conditions)
finalExpression += finalExpression || condition;
Another possible solution to this, especially when someone doesn't want to use an external library is using expression trees.
Add a following expression extension:
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);
return Expression.Lambda<Func<T, bool>>(
Expression.OrElse(expr1.Body, invokedExpr), expr1.Parameters);
}
As an example imagine you have a Container entity, which has InnerContainer nested entity with two properties Name and Id. Then you can use it in the following way:
Expression<Func<Container, bool>> whereQuery = c => c.InnerContainer.Name == "Name1";
whereQuery = whereQuery.Or(c => c.InnerContainer.Name == "Name2");
whereQuery = whereQuery.Or(c => c.InnerContainer.Id == 0);
var result = query
.Where(whereQuery)
.ToList();
Materializing such query will result in the following SQL:
SELECT [x].[Id], [x].[InnerContainerId]
FROM [Containers] AS [x]
LEFT JOIN [InnerContainer] AS [x.InnerContainer] ON [x].[InnerContainerId] = [x.InnerContainer].[Id]
WHERE [x.InnerContainer].[Name] IN (N'Name1', N'Name2') OR ([x].[InnerContainerId] = 0)
This way you can hold lambdas in a collection and loop through them.
Thanks to Raphaƫl Althaus that gave the following link:
http://www.albahari.com/nutshell/predicatebuilder.aspx
Predicate builder is the solution. You can use it installing LinqKit from Nuget. In that url you can find also the implementation of this class.
Note: in order to make this work with LinqToSql or LinqToEntities the IQueriable Object must be transformed using "AsExpandable()" method, for memory objects it's not required

Dynamic where clause for joined System.Data.DataTable in Linq C#

I'd like to create a dynamic where clause for variable amount of columns. Can this be done?
I think I've looked at all the posts related to this topic but still cannot find the answer.
var results = from table1 in dt1.AsEnumerable()
join table2 in dt2.AsEnumerable() on table1.Field<int>("ID") equals table2.Field<int>("ID")
where table1["ColumnA"].ToString() != table2["ColumnA"].ToString() ||
table1["ColumnB"].ToString() != table2["ColumnB"].ToString() ||
table1["ColumnC"].ToString() != table2["ColumnC"].ToString()
select table1;
You can logically add where clauses. Here's an extension method that does it
(it's not mine, I saw it somewhere here on StackOverflow, a long time ago. I'll reference it when I find it)
Func<T, bool> And(this Func<T, bool> a, Func<T, bool> b)
{
return (T x) => a(x) && b(x);
}
With it, you can iterate over the columns and add them to the where clause:
Func<string, bool> whereClause = x => true;
foreach(string columnName in dt.Columns.Select(x => x.ColumnName)
{
string first = dt.Columns[columnName].ToString();
string second = dt2.Columns[columnName].ToString();
whereClause = whereClause.And(x => first.Equals(second));
}
var result = (from foo in bar select fooBar).Where(whereClause);
You could use either System.Linq.Dynamic(ScottGu's blog article and nuget) in case of dynamic where clause:
var results = (from fruit in fruits
join car in cars on fruit.Id equals car.Id
select new { fruit, car })
.AsQueryable()
.Where("fruit.ColA != car.ColA")
.Where("fruit.ColB == car.ColB");
Or dynamicaly build expressions this using extensions from PredicateBuilder writen by #joe-albahari. For example:
var predicate =
PredicateBuilder
.True<Tuple<Product, Product>>()
.And(t => t.Item1.ColA != t.Item2.ColA)
.And(t => t.Item1.ColB == t.Item2.ColB)
.Compile();
(from fruit in fruits
join car in cars on fruit.Id equals car.Id
select Tuple.Create(fruit, car))
.Where(predicate)
.Dump();
ps: full code available at gisthub

LINQ BuildContainsExpression With OR conditions

I'm trying to get the following SQL query to work in LINQ:
Select id from table1 where id in (1,2) or canceledId in (1,2)
I'm using BuildContainsExpression to achieve the "IN" condition, but I can't figure out how to implement the "or" condition.
My shot in the dark is as follows:
var identifiers = new List<int> {1,2};
var query = (from t in Context.Table1
select t);
var query =
query.Where(BuildContainsExpression<Table1, int>(t => t.Id, identifiers));
if (showCanceled)
{
var expression = query.Where(BuildContainsExpression<Table1, int>(t => t.CanceledId.Value, identifiers)).Expression;
Expression.Or(expression, transactionsQuery.Expression);
}
But I get the following exception:
The binary operator Or is not defined for the types 'System.Linq.IQueryable1[Table1]' and 'System.Linq.IQueryable1[Table1]'..
Any ideas? -Am I in the right direction?
Thanks,
Nir.
You are appending your OR in the wrong place. What you are doing now is effectively something like this:
(from t in Context.Table1
where identifiers.Contains(t.Id)
select t)
OR
(where identifiers.Contains(t.CanceledId))
The second problem is that the BuildContainsExpression method you use, returns a lambda expression, something that looks like this:
t => t.Id == 1 || t.Id == 2 || ...
You can't change this expression once it's generated. However, that's what you want because you'd like to have something like this:
t => t.Id == 1 || t.Id == 2 || ... || t.CanceledId == 1 || t.CanceledId == 2 || ...
You can't simply take the body of this lambda expression and or it together with another expression because it depends on the parameter t.
So what you can do is the following:
// Overload of BuildContainsExpression.
private static Expression<Func<T, bool>> BuildOtherContainsExpression<T>(
ParameterExpression p, Expression field1, Expression field2, int[] values)
{
var eq1 = values.Select(v => Expression.Equal(field1, Expression.Constant(v)));
var eq2 = values.Select(v => Expression.Equal(field2, Expression.Constant(v)));
var body = eq1.Aggregate((acc, equal) => Expression.Or(acc, equal));
body = eq2.Aggregate(body, (acc, equal) => Expression.Or(acc, equal));
return Expression.Lambda<Func<T, bool>>(body, p);
}
// Create a parameter expression that represents something of type Table1.
var parameter = Expression.Parameter(typeof(Table1), "t");
// Create two field expressions that refer to a field of the parameter.
var idField = Expression.Property(parameter, "Id");
var canceledIdField = Expression.Property(parameter, "CanceledId");
// And finally the call to this method.
query.Where(BuildContainsExpression<Table1>(
parameter, idField, canceledIdField, identifiers));
Your if statement would now look like this:
if (!showCanceled)
{
// Use original version of BuildContainsExpression.
}
else
{
// Create some expressions and use overloaded version of BuildContainsExpression.
}
I know I'm a bit late to the party here - but I think the original code in the original poster's question was 99% right.
The only wrong was that instead of
Expression.Or
it should have been
Expression.OrElse

How to join results from two different sets in LINQ?

I get some data about customers in my database with this method:
public List<KlientViewModel> GetListOfKlientViewModel()
{
List<KlientViewModel> list = _klientRepository.List().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
}).ToList();
return list;
}
but also I have another method which counts value for extra field in KlientViewModel - field called 'Naleznosci'.
I have another method which counts value for this field based on customers ids, it looks like this:
public Dictionary<int, decimal> GetNaleznosc(List<int> klientIds)
{
return klientIds.ToDictionary(klientId => klientId, klientId => (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
So what I want to do is to join data from method GetNaleznosc with data generated in method GetListOfKlientViewModel. I call GetNaleznosc like this:
GetNaleznosc(list.Select(k => k.Id).ToList())
but don't know what to do next.
Having obtained the dictionary:
var dict = GetNaleznosc(list.Select(k => k.Id).ToList());
You can now efficiently look up the decimal value of Naleznosci for a given client:
foreach (var k in list)
k.Naleznosci = dict[k.Id];
Now you have merged the values into the main list. Is this what you mean?
By the way, in your function that builds the dictionary, you make it accept a List<int>, but then all you do is call ToDictionary on it, which only requires IEnumerable<int>. So change the parameter type to that, and then you can call it:
var dict = GetNaleznosc(list.Select(k => k.Id));
This removes the call to ToList, which avoids making an unnecessary intermediate copy of the whole list of Ids. Probably won't make much difference in this case if you're hitting a database and then building up a large set of results in memory, but maybe worth bearing in mind for other uses of these operations.
Also, looking again at the helper function, there is no apparent advantage in building up the set of results in a dictionary for a list of ids, because each one is handled independently. You could simply put:
public decimal GetNaleznosc(int klientId)
{
return (from z in _zdarzenieRepository.List()
from c in z.Klient.Cennik
where z.TypZdarzenia == (int) TypyZdarzen.Sprzedaz && z.IdTowar == c.IdTowar && z.Sprzedaz.Data >= c.Od && (z.Sprzedaz.Data < c.Do || c.Do == null) && z.Klient.Id == klientId
select z.Ilosc*(z.Kwota > 0 ? z.Kwota : c.Cena)).Sum() ?? 0);
}
That is, provide a function that discovers just one value. Now you can directly build the right list:
public List<KlientViewModel> GetListOfKlientViewModel()
{
return _klientRepository.List().AsEnumerable().Select(k => new KlientViewModel
{
Id = k.Id,
Imie = k.Imie,
Nazwisko = k.Nazwisko,
Nazwa = k.Nazwa,
SposobPlatnosci = k.SposobPlatnosci,
Naleznosci = GetNaleznosc(k.Id)
}).ToList();
}

Resources