Linq Dynamically append Where clauses with OR - c#

I've been trying to build a linq query with dynamically added where clauses. I have a web page with a bunch of checkboxes that are selected to correspond to which fields you want to search:
What I have so far is the following:
//This calls a select from table to construct the query which the where clauses will be added to
IQueryable<AutoCompleteRestultDto> query = GetAutocompleteResults();
if (FirstName == true || AllFields == true)
{
Expression<Func<AutoCompleteRestultDto, bool>> firstNameFilter = c => terms.Any(t => c.FirstName.ToLower().Contains(t.ToLower()));
query = query.Where(firstNameFilter);
}
if (LastName == true || AllFields == true)
{
Expression<Func<AutoCompleteRestultDto, bool>> lastNameFilter = c => terms.Any(t => c.LastName.ToLower().Contains(t.ToLower()));
query = query.Where(lastNameFilter);
}
if (KnownAs == true || AllFields == true)
{
Expression<Func<AutoCompleteRestultDto, bool>> knownAsFilter = c => terms.Any(t => c.KnownAs.ToLower().Contains(t.ToLower()));
query = query.Where(knownAsFilter);
}
// etc.
return query
.Select(c => new ContactAutoCompleteModel
{
label = c.FirstName + " " + c.LastName
})
.Take(15)
.OrderBy(d => d.label)
.ToList();
The problem is this solution requires all the expressions that are tacked on to simultaneously be true ie: where(clause1 AND clause2 AND clause3)
Can't figure out how to change this to OR clauses ie: where(clause1 OR clause2 OR clause3)

You are chaining Enumerable.Where calls in the code you posted. That's why you get the AND effect. This below is a poach of PredicateBuilder using predicates instead of expressions.
public static class PredicateExtensions
{
public static Predicate<T> Or<T> (this Predicate<T> p1, Predicate<T> p2)
{
return obj => p1(obj) || p2(obj);
}
public static Predicate<T> And<T> (this Predicate<T> p1, Predicate<T> p2)
{
return obj => p1(obj) && p2(obj);
}
public static Predicate<T> False<T> () { return obj => false; }
public static Predicate<T> True<T> () { return obj => true; }
public static Predicate<T> OrAll<T> (IEnumerable<Predicate<T>> conditions)
{
Predicate<T> result = PredicateExtensions.False<T>();
foreach (Predicate<T> cond in conditions)
result = result.Or<T>(cond);
return result;
}
public static Predicate<T> AndAll<T> (IEnumerable<Predicate<T>> conditions)
{
Predicate<T> result = PredicateExtensions.True<T>();
foreach (Predicate<T> cond in conditions)
result = result.And<T>(cond);
return result;
}
}
You can use the above like so with an enumerable , customizing (apriori) the predicate enumerable on some condition:
Predicate<AutoCompleteRestultDto> firstNamePredicate =
c => terms.Any(t => c.FirstName.ToLower().Contains(t.ToLower()));
Predicate<AutoCompleteRestultDto> lastNamePredicate =
c => terms.Any(t => c.LastName.ToLower().Contains(t.ToLower()));
Predicate<AutoCompleteRestultDto> knownAsPredicate =
c => terms.Any(t => c.KnownAs.ToLower().Contains(t.ToLower()));
var all = new Predicate<AutoCompleteRestultDto>[] {
firstNamePredicate,
knownAsPredicate,
lastNamePredicate };
//
var items = query.Where(a => PredicateExtensions.OrAll(all)(a)).ToList();
items = query.Where(a => PredicateExtensions.AndAll(all)(a)).ToList();
or adding them iteratively like you do, step by step:
Predicate<AutoCompleteRestultDto> orResultPredicate =
PredicateExtensions.False<AutoCompleteRestultDto>();
if (FirstName == true || AllFields == true) {
orResultPredicate=orResultPredicate.Or(firstNamePredicate); }
if (LastName == true || AllFields == true) {
orResultPredicate = orResultPredicate.Or(lastNamePredicate); }
if (KnownAs == true || AllFields == true) {
orResultPredicate = orResultPredicate.Or(knownAsPredicate); }
Func<AutoCompleteRestultDto, bool> funcOr = c => orResultPredicate(c);
//
IQueryable<AutoCompleteRestultDto> query; // initialized already
var items = query.Where(funcOr).ToList();

Related

Linq if else condition

How can I merge this below query into a single query? I think multiple if's will add unnecessarily to code complexity.
var query = scenario.Where(x => x.ScenarioNumber == ScenarioNumber).Select(x => x).Distinct().ToList();
// Match exception number else return wild card entries.
if(query.Any(x=>x.ExcepNumber == ExcepNumber))
{
query = query.Where(x => x.ExcepNumber == ExcepNumber).ToList();
}
else
{
query = query.Where(x => x.ExcepNumber == "**").ToList();
}
// Match regime else return wild card entries.
if (query.Any(x => x.Regime == Regime))
{
query = query.Where(x => x.Regime == Regime).ToList();
}
else
{
query = query.Where(x => x.Regime == "**").ToList();
}
finalResponse = query.Select(x => x.TestColumn).Distinct().ToList();
I have a suggestion. You could extract it into a generic method, in which you would pass a selector function which specifies the property to be used for the filtering and the 2 choices that you would like to be compared (and the collection of course).
private List<T> EitherFilterOrWildCard<T>(Func<T, string> selector, string compare, string elseCompare, List<T> query)
{
var temp = query.Where(x => selector(x) == compare);
return temp.Any() ? temp.ToList() : query.Where(x => selector(x) == elseCompare).ToList();
}
Your if statements would then shrink to these 2 lines:
query = EitherFilterOrWildCard<MyClass>(x => x.ExcepNumber, ExcepNumber, "**", query);
query = EitherFilterOrWildCard<MyClass>(x => x.Regime, Regime, "**", query);

Dynamic Expression Parameter

How can i make dynamic expression parameter ?
i was try
public List<Station> GetStationsByTimeAndState(StationState stationState, int? categoryId = null, int? provinceId = null, int? districtId = null)
{
List<Station> stations;
Expression<Func<Station, bool>> exp = p => p.StationState == stationState;
if(categoryId==null){
exp+= p.CategoryId==categoryId;//or exp.Add()//exp.Update()
}
.....
return stations = stationDal.GetList(exp);
}
The GetList method calls the Where query that belongs to Linq in itself.
What I want to do is dynamically constructing and sending the expression inside the Where query.
You need to build a Linq Expression Tree for your example this would look like this:
// Parameter: p
Expression<Func<Station, bool>> exp;
var parameterExpression = Expression.Parameter(typeof(Station));
// p == stationState
var equalsStationState = Expression.Equal(
Expression.Property(stateParameterExpression, nameof(Station.StationState))
Expression.Constant(stationState));
if (categoryId != null)
{
// p.CategoryId == categoryId
var equalsCategory = Expression.Equal(
Expression.Property(stateParameterExpression, nameof(Station.CategoryId)),
Expression.Constant(categoryId));
// (p == stationState) && (p.CategoryId == categoryId)
var andExpression = Expression.AndAlso(equalsStationState, equalsCategory);
// p => (p == stationState) && (p.CategoryId == categoryId)
exp = predicate = Expression.Lambda(
andExpression,
stateParameterExpression);
}
The code above is not complete, but you can get an idea on how it works.
As an alternative you could conditionally chain Where methods, which would be much simpler:
IEnumerable<StationList> stations = allStations
.Where(s => s.StationState == stationState);
if (categoryId != null)
{
stations = stations.Where(s => s.CategoryId == category)
}
return stations;

using QueryOver's query separated

How can I use QueryOver's linq query separated like below ICriteria which will be used in a generic method?
ICriterion c = Restrictions.And(Restrictions.Eq("Name", "Foo"),
Restrictions.Or(Restrictions.Gt("Age", 21), Restrictions.Eq("HasCar",true)));
IQueryOver<Foo> c = session.QueryOver<Foo>().Where((k => k.Name == "Tiddles"
&& k => k.Age== 21) || k => k.Age < 21);
public static IList<T> All(ICriterion c)
{
using (var session = NHibernateHelper<T>.OpenSession())
{
var CC = session.CreateCriteria(typeof(T));
CC.Add(c);
return CC.List<T>();
}
}
The equivalent to the first line, in lambda form is
ICriterion cr = Restrictions.Where<Foo>(k => k.Name == "Foo" && (k.Age > 21 || k.HasCar));
You could pass in a Expression<Func<T, bool>> which is what one of the Where overloads accepts instead of a Criteria.
System.Linq.Expressions.Expression<Func<Foo, bool>> c = k =>
(k.Name == "Tiddles" && k.Age== 21) || k.Age < 21;
public static IList<T> All(Expression<Func<T, bool>> expression)
{
using (var session = NHibernateHelper<T>.OpenSession())
{
return session.QueryOver<T>()
.Where(expression)
.List();
}
}

How to convert IQueryable<T> to Expression<Func<T, bool>>?

I just want to build a dynamic filters.
And finally to return
Expression<Func<Event, bool>>
I've tried to use the Combine (AndAlso) expressions, but it wasn't workin and finally I found that there are IQueryable queries which works good, but now how can I convert it to the return type -
Expression<Func<Event, bool>>?
My code:
public IQueryable<Event> GetBySearch(EventFilter search)
{
IQueryable<Event> query = this.Context.Events.AsQueryable();
Expression<Func<Event, bool>> expression = null;
if (search.CategoryId != 0)
{
query = query.Where(x => x.CategoryId == search.CategoryId);
}
if (search.SubCategoryId != 0)
{
query = query.Where(x => x.SubCategoryId == search.SubCategoryId);
}
expression = query.Expression as Expression<Func<Event, bool>>; //This convert is not working, it returns null.
return this.Context.Events.Where(expression);
}
Any reason you don't just do the following:
public IQueryable<Event> GetBySearch(EventFilter search)
{
IQueryable<Event> query = this.Context.Events.AsQueryable();
if (search.CategoryId != 0)
{
query = query.Where(x => x.CategoryId == search.CategoryId);
}
if (search.SubCategoryId != 0)
{
query = query.Where(x => x.SubCategoryId == search.SubCategoryId);
}
return query;
}
As Florian said in the comment, returning IQueryables is to be avoided (when possible). The easy solution is to return a list instead:
public List<Event> GetBySearch(EventFilter search)
{
IQueryable<Event> query = this.Context.Events.AsQueryable();
if (search.CategoryId != 0)
{
query = query.Where(x => x.CategoryId == search.CategoryId);
}
if (search.SubCategoryId != 0)
{
query = query.Where(x => x.SubCategoryId == search.SubCategoryId);
}
return query.ToList();
}
This conversion is not valid because Where converts it into a MethodCallExpression
This would be valid:
MethodCallExpression e = query.Expression as MethodCallExpression;

How to build up a dynamic OR query in LINQ?

I'm trying to dynamically create a where clause with link.
Now, creating something like SELECT Product WHERE x = 123 AND y = 349 AND w = 948 is no problem. I'm just adding extra Where filters.
products = products.Where(product => product.Property == 123);
But now i have an unknown number of values that should be tested with an OR operator. The SQL Should be like SELECT Product WHERE x = 123 OR x = 499 OR x = 998 .. and n more OR's
Build a list of numbers to check and do something like the following:
var idsToCheck = new List<int>{123, 789, 654}; //Build from dynamic list
products = products.Where(x => idsToCheck.Contains(x.Id));
The above can be duplicated for v and w if required
var idsToCheck = new List<int>{123, 789, 654}; //Build from dynamic list
products = products.Where(x => idsToCheck.Contains(x.Id));
var propertyToCheck = new List<int>{123, 789, 654};//Build from dynamic list
products = products.Where(x => propertyToCheck.Contains(x.Property));
var numberToCheck = new List<int>{123, 789, 654};
products = products.Where(x => numberToCheck.Contains(x.Number));
If your values to check are already being passed in as enumerables then you don't need to build your xxxToCheck collections. You can simply do the .Contains against the originals
You can use contains on a list or just have the equation like so:
products.Where(product => (product.Property == 123) || (product.Property == 499) || (product.Property == 998));
If you want to compose an OR query from multiple where clauses you really need to compose the predicates in the where clauses instead. Afterall, chaining where clauses would be an AND.
So, if you have the following predicates:
Func<Product, bool> w1 = p => p.x == 123;
Func<Product, bool> w2 = p => p.y == 349;
Func<Product, bool> w3 = p => p.w == 948;
Func<Product, bool> w4 = p => p.Property == 123;
Func<Product, bool> w5 = p => p.x == 499;
Func<Product, bool> w6 = p => p.x == 998;
You can define the following extension method to OR them:
public static Func<T, bool> Or<T>(this Func<T, bool> #this, Func<T, bool> that)
{
return t => #this(t) || that(t);
}
Now you could write:
var products1 = products.Where(w1.Or(w5).Or(w6));
var products2 = products.Where(
((Func<Product, bool>)(p => p.x == 123))
.Or(p => p.y == 349)
.Or(p => p.Property == 123));
If you want to work with lists of clauses then add this extension method:
public static Func<T, bool> Or<T>(this IEnumerable<Func<T, bool>> #this)
{
return #this.Aggregate((Func<T, bool>)(t => true), (a, t) => a.Or(t));
}
Now you can write:
var predicates = new List<Func<Product, bool>>()
{
p => p.x == 123,
p => p.w == 948,
p => p.Property == 123,
p => p.x == 998,
};
var products3 = products.Where(predicates.Or());
If you're writing this against an IQueryable<T> provider then you need to work with Expression<Func<Product, bool>> and not just Func<Product, bool>.
Then you need these extension methods:
public static Expression<Func<T, bool>> Or<T>(
this Expression<Func<T, bool>> #this,
Expression<Func<T, bool>> that)
{
var param =
Expression
.Parameter(typeof(T), "t");
var body =
Expression
.Or(
Expression
.Invoke(#this, param),
Expression
.Invoke(that, param));
return
Expression
.Lambda<Func<T, bool>>(body, param);
}
public static Expression<Func<T, bool>> Or<T>(
this IEnumerable<Expression<Func<T, bool>>> #this)
{
return #this.Aggregate((Expression<Func<T, bool>>)(t => true), (a, t) => a.Or(t));
}
The calling code still works the same:
var predicatesX = new List<Expression<Func<Product, bool>>>()
{
p => p.x == 123,
p => p.w == 948,
p => p.Property == 123,
p => p.x == 998,
};
var products3X = products.Where(predicatesX.Or());
Is this what you were looking for?

Categories