Add additional WHERE clause to existing Linq query - c#

I have a method which accepts a linq query:
public IEnumerable<User> GetAll(System.Linq.Expressions.Expression<Func<UserDTO, bool>> query)
{
return GetAllDTO(query);
}
What I would like to be able to do is append an additional WHERE clause to this existing query so it looks something like this:
public IEnumerable<User> GetAll(System.Linq.Expressions.Expression<Func<UserDTO, bool>> query)
{
return GetAllDTO(query).Where(x => x.Organisation == "something")
}
But this will load ALL the records and that match the query and THEN apply the where clause. I want to add the where clause to the original query so that only the records matching both are returned.

This example modifies the query before executing it:
private IEnumerable<int> GetAll(Expression<Func<int, bool>> currentQuery)
{
Expression left = currentQuery.Body;
BinaryExpression right = Expression.GreaterThan(
currentQuery.Parameters[0], Expression.Constant(0));
BinaryExpression combined = Expression.AndAlso(left, right);
Expression<Func<int, bool>> final = Expression.Lambda<Func<int, bool>>(
combined, currentQuery.Parameters[0]);
return GetAllInt(final);
}
If currentQuery starts as x => x != 5, the function above will return x => (x != 5) && (x > 0).
Here's the remaining example code:
private static readonly List<int> TheList =
new List<int> { 0, 1, 0, 2, 0, 3, 0, 4, 0, 5 };
public static void Main(string[] args)
{
Expression<Func<int, bool>> initialQuery = x => x != 5;
IEnumerable<int> result = GetAll(initialQuery);
foreach (int i in result)
{
Console.WriteLine(i);
}
Console.ReadLine();
}
And the GetAllInt method:
private static IEnumerable<int> GetAllInt(Expression<Func<int, bool>> query)
{
return TheList.Where(query.Compile());
}
This prints out:
1
2
3
4
This may not fit your situation exactly but should at least give you a starting point.

In the end I managed it like this:
public IEnumerable<User> GetAll(System.Linq.Expressions.Expression<Func<UserDTO, bool>> query)
{
var prefix = query.Compile();
query = c => prefix(c) && c.Organisation == organisationID;
}

Related

EF LINQ query with Expression: method name expected - How can I pass an Expression into function to be used in EF query?

I have a function that's supposed to return different info from an EF LINQ query based on an Expression and/or lambda that's passed to it.
Here's my code:
public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year,
Expression<Func<string, bool>> lambStat)
{
var ctx = new MI_Entities(server, database);
var strats = from y in ctx.SIset.AsNoTracking()
where y.Jurisd == jurisd && y.Year_ID == Year && lambStat(y.Status)
select y;
return new ObservableCollection<SI>(strats);
}
The compiler gives me the following error:
Method name expected
If I use this instead:
public static ObservableCollection<SI> GetDisplayStratsByJurisd(string jurisd, short Year,
Expression<Func<string, bool>> lambStat)
{
var ctx = new MI_Entities(server, database);
Func<string, bool> bob = lambStat.Compile();
var strats = from y in ctx.SIset.AsNoTracking()
where y.Jurisd == jurisd && y.Year_ID == Year && bob(y.Status)
select y;
return new ObservableCollection<SI>(strats);
}
Then I get a different error:
The LINQ expression node type 'Invoke' is not supported in LINQ to Entities
So, I'm not sure how to go about passing a lambda to a function so that it can be used in a query. Can this be done? If so, how?
So you have some Expression<Func<TSource, bool>>, and you want to combine them into one Expression<Func<TSource, bool>> using AND functionality, such that you can use it AsQueryable in entity framework.
It would be nice to have this in a LINQ like fashion, so we can put it in between a concatenation of Linq statements.
Let's create some extension functions.
// A function that takes two Expression<Func<TSource, bool>> and returns the AND expression
static Expression<Func<TSource, bool>> AndAlso<TSource> (
this Expression<Func<TSource, bool>> x,
Expression<Func<TSource, bool>> y)
{
// TODO implement
}
Usage:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
Expression<Func<Student, bool>> exprAND = expr1.AndAlso(expr2);
var brummyMaleStudents = dbContext.Students.Where(exprAnd).Select(...);
Let's implement AndAlso
Normally a Where would be like:
.Where(student => student.City == "Birmingham" && student.Gender == Gender.Male)
The input parameter student is used as input for the left expression and as input for the right expression. We need to have something that says:
Take one input parameter of type Student, put it in the left expression, and in the right Expression and perform an AND between the two Boolean return values. Return the Boolean result.
For this we create a class derived from System.Linq.Expressions.ExpressionVisitor.
This class represent the action: "put a student in the expression and calculate it". This calculating is called "visiting the Expression". The input of the expression is an expression, the result of visiting is another expression:
internal class ReplaceExpressionVisitor : ExpressionVisitor
{
private readonly Expression oldValue;
private readonly Expression newValue;
public ReplaceExpressionVisitor(ParameterExpression oldValue,
ParameterExpression newValue)
{
this.oldValue = oldValue;
this.newValue = newValue;
}
public override Expression Visit(Expression node)
{
if (node == this.oldValue)
{ // "my" expression is visited
return this.newValue;
}
else
{ // not my Expression, I don't know how to Visit it, let the base class handle this
return base.Visit(node);
}
}
}
Now that we've create the expression visitor we can implement AndAlso:
static Expression<Func<TSource, bool>> AndAlso<TSource>(
this Expression<Func<TSource, bool>> expr1,
Expression<Func<TSource, bool>> expr2)
{
// Create one expression that represent expr1 && expr2
// the input of expr1 is a TSource,
// the input of expr2 is a TSource,
// so the input of expr1 && expr2 is a TSource:
ParameterExpression inputParameter = Expression.Parameter(typeof(TSource));
// Visit the left part of the AND:
var leftVisitor = new ReplaceExpressionVisitor(expr1.Parameters[0], inputParameter)
var left = leftVisitor.Visit(expr1.Body);
// Visit the right part of the AND:
var rightVisitor = new ReplaceExpressionVisitor(expr2.Parameters[0], inputParameter);
var right = rightVisitor.Visit(expr2.Body);
// Combine left and right with a binary expression representing left && right:
var andExpression = Expression.AndAlso(left, right);
// return the lambda expression that takes one Tsource as input and returns the AND:
var lambda = Expression.Lambda<Func<TSource, bool>>(andExpression, new[] {parameter});
return lambda;
}
Usage:
Expression<Func<Student, bool>> expr1 = student => student.City == "Birmingham";
Expression<Func<Student, bool>> expr2 = student => student.Gender == Gender.Male;
var brummyMaleStudents = dbContext.Students.Where(expr1.AndAlso(expr2));
Another example:
IQueryable<int> numbers = Enumerable.Range(0, 100).Asqueryable();
// two expressions: one that filters 0, 2, 4, 6, 8, ...
// and one that filters 0, 3, 6, 9, 12, ...
Expression<Func<int, bool>> exprEvenNumbers = x => x % 2 == 0;
Expression<Func<int, bool>> exprTrifolds = x => x % 3 == 0;
// the AND of these two expressions should filter 0, 6, 12, 18, ...
Expression<Func<int, bool>> exprSixfolds = exprEvenNumbers.AndAlso(exprTrifolds);
// Test this
IQueryable<int> sixfolds = numbers.Where(exprSixfolds);
IEnumerable<int> results = sixfolds.Take(10).AsEnumerable();
foreach (int i in results)
{
Console.WriteLine(i);
}
This will print the numbers 0, 6, 12, 18, ...

Combine expressions for Where statement [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
How do I combine LINQ expressions into one?
public bool IsUnique(params Expression<Func<Employee, bool>>[] properties)
{
var combinedProperties = Combine(properties);
var rowCount = _session.QueryOver<Employee>().Where(combinedProperties).ToRowCountQuery().RowCount();
return rowCount == 0;
}
Expression<Func<Employee, bool>> Combine(Expression<Func<Employee, bool>>[] properties)
{
???
}
Usage:
var isUnique = _employeeRepository.IsUnique(x => x.FirstName == commandMessage.FirstName, x => x.LastName == commandMessage.LastName);
Is there a way of combining predicates with an AND operator?
The simplest way would be to loop over your params array and call .Where for each expression.
Pseudo
var query = _session.QueryOver<Employee>()
for each expression in expressions
query = query.Where(expression)
I know this isn't precisely what you asked, but it may be good enough as it achieves the overall goal?
I think an extension method would be more useful and will work with all your IEnumerable queries:
public static class MyExtensions
{
// usage:
// myList.CombinedWhere(x => x.Name == "John", x => x.City == "Miami", x => x.Code > 5);
public static IEnumerable<T> CombinedWhere<T>(this IEnumerable<T> source,
params Func<T, bool>[] predicates)
{
var query = source.Where(l => true);
foreach(var pred in predicates)
{
query = query.Where (pred);
}
return query;
}
}
Use this just like you would use the Where extension except you can use a variable number of arguments.
With the addition of the above extension, your code changes slightly:
public bool IsUnique(params Func<Employee, bool>[] predicates)
{
var rowCount = _session.QueryOver<Employee>()
.CombinedWhere(predicates).ToRowCountQuery().RowCount();
return rowCount == 0;
}
var isUnique = _employeeRepository.IsUnique(
x => x.FirstName == commandMessage.FirstName,
x => x.LastName == commandMessage.LastName);
Actually, now that I look at it, you might just be able to boil it down to one expression:
var isUnique = (_session.QueryOver<Employee>()
.CombinedWhere(
x => x.FirstName == commandMessage.FirstName,
x => x.LastName == commandMessage.LastName)
.ToRowCountQuery()
.RowCount()) == 0; // == 1?
Yes you can use LinqKit with .Invoke()
Expression<Func<Purchase,bool>> criteria1 = p => p.Price > 1000;
Expression<Func<Purchase,bool>> criteria2 = p => criteria1.Invoke (p)
|| p.Description.Contains ("a");
Thanks to Brad Rem and Kenneth Ito they gave me some inspirations.
Here is the solution that works for NHibernate's IQueryOver API.
Repository:
public bool IsUnique(int id, params Expression<Func<T, bool>>[] properties)
{
var rowCount = _session.QueryOver<T>().CombinedWhere(properties).ToRowCountQuery().RowCount();
// create
if (id == 0)
{
return rowCount == 0;
}
// update
return rowCount <= 1;
}
IQueryOver Extension:
public static class IQueryOverExtension
{
public static IQueryOver<T, T> CombinedWhere<T>(this IQueryOver<T, T> source, params Expression<Func<T, bool>>[] predicates)
{
return predicates.Aggregate(source, (current, predicate) => current.Where(predicate));
}
}

Is there any way to pass the lambda expression as a variable or argument?

I need to pass the lambda query as a parameter, the followings code is sample and I am interesting to find an implement for it, there is samples: some thing like this:
var expr1 = Where(n => n > 6).OrderBy(n => n % 2 == 0).Select(n => n);
var expr2 = TakeWhile((n, index) => n >= index));
And Use it Like this:
public void UseLambda<T> (IEnumerable<T> source , lambda Expr){
var items= Expr.Compile(source);
foreach(var item in items)
Console.Writeline(item.ToString());
}
public void Main(){
List<int> numbers = new List<int> { 10, 24, 9, 87, 193, 12, 7, 2, -45, -2, 9 };
var expr1 = Where(n => n > 6).OrderBy(n => n % 2 == 0).Select(n => n);
UseLambda(numbers, expr1);
}
Does any one have an idea about it?
Check Func(Of T, TResult) Delegate (MSDN)
using System;
public class LambdaExpression
{
public static void Main()
{
Func<string, string> convert = s => s.ToUpper();
string name = "Dakota";
Console.WriteLine(convert(name));
}
}
From MSDN
The underlying type of a lambda expression is one of the generic Func delegates. This makes it possible to pass a lambda expression as a parameter without explicitly assigning it to a delegate. In particular, because many methods of types in the System.Linq namespace have Func(Of T, TResult) parameters, you can pass these methods a lambda expression without explicitly instantiating a Func(Of T, TResult) delegate.
EDIT
Possible solution for your case
static void Main(string[] args)
{
List<int> numbers = new List<int> { 10, 24, 9, 87, 193, 12, 7, 2, -45, -2, 9 };
Func<IEnumerable<int>, IEnumerable<int>> expr = n => n.Where(n1 => n1 > 6).OrderBy(n1 => n1 % 2 == 0).Select(n1 => n1);
UseLambda<int>(numbers, expr);
}
private static void UseLambda<T>(List<T> numbers,
Func<IEnumerable<T>,
IEnumerable<T>> expr)
{
var values = expr(numbers);
foreach (var item in values) {
Console.WriteLine(item);
}
}
If you define your LINQ expressions like this:
Func<IEnumerable<int>, IEnumerable<int>> expr1 =
l => l.Where(n => n > 6).OrderBy(n => n % 2 == 0).Select(n => n);
Func<IEnumerable<int>, IEnumerable<int>> expr2 =
l => l.TakeWhile((n, index) => n >= index);
And your UseLambda method as:
public void UseLambda<T> (IEnumerable<T> source
,Func<IEnumerable<T>, IEnumerable<T>> lambda)
{
var items= lambda(source);
foreach(var item in items)
Console.Writeline(item.ToString());
}
}
Then you I think you have what you're looking for.
Do you mean something like this:
public void UseLambda<T> (IEnumerable<T> source , Func<T, bool> where, Func<T, bool> order)
{
if(source != null)
{
IOrderedEnumerable<T> orderBy = source.Where(where).OrderBy(order);
foreach (T value in orderBy)
{
Console.WriteLine(value);
}
}
}
So that you could call it like so:
UseLambda(numbers, x => x > 6, x => x % 2 == 0);
Well, a lambda is nothing but a delegate, so you could have a method like this:
public void DoIt(IEnumerable a, Action<IEnumerable> performThis)
{
performThis(a);
}
But where's the sense in it? Instead of calling a method that applies your lambda, why not calling it directly as you do in the last lines of your code?
public void UseLambda<T>(IEnumerable<T> source, Expression<Func<IEnumerable<T>, IEnumerable<T>>> expr)
{
var items = expr.Compile();
foreach (var item in items.Invoke(source))
{
Console.WriteLine(item.ToString());
}
}
public void Main()
{
Expression<Func<IEnumerable<int>, IEnumerable<int>>> expr = s => s.Where(n => n > 6).OrderBy(n => n % 2 == 0).Select(n => n);
var list = new List<int> { 10, 24, 9, 87, 193, 12, 7, 2, -45, -2, 9 };
UseLambda(list, expr);
}

Are Linq-To-Sql Dynamic-Where-Clauses Even Possible in Framework 3.5?

UPDATE: It Is Now Working
I was able to finally get it completed. A working-example is detailed in an answer below (which I will be able to mark-off in 2 days).
Everything Below Here Was Part of the Original Question
For the last 3 days, I have been trying to build a dynamic-where-clause on a DBML DataContext using code samples from questions posted here and from other sources as well...none have worked!
For the reasons below, I am beginning to wonder if this is even POSSIBLE using under Framework 3.5:
Predicate Builder notes Framework 4.0 on their site.
Some answers here talk about an equivolent Invoke versions in 4.0 (so I have some hope here).
...I could go on but you get the idea.
I am really at a loss and seem to be "grabbing at strings"...and I need some sound advice on how to approach this.
Original Version Had SOME Success But Only When:
The ONLY time I had a 'inkling' of success the data came-up (all 6178 rows of it) but no WHERE CLAUSE was applied. This was evidenced by the lack of any WHERE CLAUSE applied into the SQL found in the dataContext.GetCommand(query).CommandText.
Other Version #1 Fails:
And generates this error: "Method 'System.Object DynamicInvoke(System.Object[])' has no supported translation to SQL."
// VERSION 1:
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 Expression<Func<T, bool>> StringLike<T>(Expression<Func<T, string>> selector, string pattern)
{
var predicate = PredicateBuilder.True<T>();
var parts = pattern.Split('%');
if (parts.Length == 1) // not '%' sign
{
predicate = predicate.And(s => selector.Compile()(s) == pattern);
}
else
{
for (int i = 0; i < parts.Length; i++)
{
string p = parts[i];
if (p.Length > 0)
{
if (i == 0)
{
predicate = predicate.And(s => selector.Compile()(s).StartsWith(p));
}
else if (i == parts.Length - 1)
{
predicate = predicate.And(s => selector.Compile()(s).EndsWith(p));
}
else
{
predicate = predicate.And(s => selector.Compile()(s).Contains(p));
}
}
}
}
return predicate;
}
}
// VERSION 1:
public List<QuickFindResult> QueryDocuments(string searchText, string customerSiteId, List<int> filterIds)
{
var where = PredicateBuilder.True<vw_QuickFindResult>();
var searches = new List<String>(searchText.Split(' '));
searches.ForEach(productName =>
{
string like = productName.Replace('"', '%')
.Replace('*', '%');
where = PredicateBuilder.StringLike<vw_QuickFindResult>(x => x.DocumentName, like);
});
var results = DocumentCollectionService.ListQuickFind(where, null);
// Do other stuff here...
return results;
}
// VERSION 1:
public static List<vw_QuickFindResult> ListQuickFind(Expression<Func<vw_QuickFindResult, bool>> where, Expression<Func<vw_QuickFindResult, bool>> orderBy)
{
var connectionString = GetConnectionString(ES_DOCUMENTS_CONNECTION_NAME);
List<vw_QuickFindResult> results = null;
using (HostingEnvironment.Impersonate())
{
using (var dataContext = new ES_DocumentsDataContext(connectionString))
{
IQueryable<vw_QuickFindResult> query = dataContext.vw_QuickFindResults;
query = query.Where(where);
results = query.ToList();
}
}
return results;
}
Other Version #2 Fails:
And generates this error: "Method 'Boolean Like(System.String, System.String)' cannot be used on the client; it is only for translation to SQL."
// VERSION 2:
public List<QuickFindResult> QueryDocuments(string searchText, string customerSiteId, List<int> filterIds)
{
Func<vw_QuickFindResult, bool> where = null;
Func<string, Func<vw_QuickFindResult, bool>> buildKeywordPredicate = like => x => SqlMethods.Like(x.DocumentName, like);
Func<Func<vw_QuickFindResult, bool>, Func<vw_QuickFindResult, bool>, Func<vw_QuickFindResult, bool>> buildOrPredicate = (pred1, pred2) => x => pred1(x) || pred2(x);
// Build LIKE Clause for the WHERE
var searches = new List<String>(searchText.Split(' '));
searches.ForEach(productName =>
{
string like = productName.Replace('"', '%')
.Replace('*', '%');
where = (where == null) ? buildKeywordPredicate(like) : buildOrPredicate(where, buildKeywordPredicate(like));
});
var results = DocumentCollectionService.ListQuickFind(where, null);
// Do other stuff here...
return results;
}
// VERSION 2:
public static List<vw_QuickFindResult> ListQuickFind(Expression<Func<vw_QuickFindResult, bool>> where, Expression<Func<vw_QuickFindResult, bool>> orderBy)
{
var connectionString = GetConnectionString(ES_DOCUMENTS_CONNECTION_NAME);
List<vw_QuickFindResult> results = null;
using (HostingEnvironment.Impersonate())
{
using (var dataContext = new ES_DocumentsDataContext(connectionString))
{
var query = dataContext.vw_QuickFindResults.AsEnumerable();
query = query.Where(where);
results = query.ToList();
}
}
return results;
}
Did you try building the query yourself using only Exression classes?
There should be no particular problems there. It is actually relatively easy to learn.
You can write a sample query, and then in debugging see how it is composed:
Expression<Func<string, bool>> exp = (s) => s.Contains("your query");
Then simply look at the exp variable in the watch, and you can see the structure.
This particular example should be composed like this:
Expression constant = Expression.Constant("your query");
Expression p = Expression.Param(typeof(string);
Expression contains = Expression.Call(p, "Contains", constant);
Expression<Func<string, bool>> lambda = Expression.Lamba(contains, p);
// Now you can send this to your ORM
For what I can tell you, I have used LinqKit and PredicateBuilder back in early 2010 with .Net 3.5, EF 1.0 and EF Poco Adapter. Back then, LinqKit was compiled for Net 3.5
Maybe if you ask the author (Albahari), he could send you (or post on the site) the 3.5 version of that. I don't have it anymore because it is in projects at my old workplace and I don't have access to them.
As a side note, I feel your pain being forced to work with 3.5 after almost 2 years of .Net 4 being around.
THIS IS THE CORRECT ASWER
Here is the working version for those who need it. The issue was a COMBINATION of things. The first of which was the following line was set to True:
var where = PredicateBuilder.True<vw_QuickFindResult>();
It should be False...
var where = PredicateBuilder.False<vw_QuickFindResult>();
I don't know why...but other changes were needed also.
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 List<QuickFindResult> QueryDocuments(string searchText, string customerSiteId, List<int> filterIds)
{
var wildCards = new string[] { "*", "\"" };
var where = PredicateBuilder.False<vw_QuickFindResult>();
var searches = new List<String>(searchText.Split(' ')); // TODO: <-- If more complex searches are needed we'll have to use RegularExpressions
// SEARCH TEXT - WHERE Clause
searches.ForEach(productName =>
{
Boolean hasWildCards = (productName.IndexOfAny(new char[] { '"', '*' }) != -1);
if (hasWildCards)
{
Int32 length = productName.Length;
if (length > 1)
{
string like = productName.Replace("%", "")
.Replace("*", "");
string first = productName.Substring(0, 1);
string last = productName.Substring(length - 1);
// Contains
if (wildCards.Contains(first) && wildCards.Contains(last))
where = where.Or(p => p.DocumentName.Contains(like) ||
p.DocumentTitle.Contains(like));
// EndsWith
else if (wildCards.Contains(first))
where = where.Or(p => p.DocumentName.EndsWith(like) ||
p.DocumentTitle.EndsWith(like));
// StartsWith
else if (wildCards.Contains(last))
where = where.Or(p => p.DocumentName.StartsWith(like) ||
p.DocumentTitle.StartsWith(like));
// Contains (default)
else
where = where.Or(p => p.DocumentName.Contains(like) ||
p.DocumentTitle.Contains(like));
}
else // Can only perform a "contains"
where = where.Or(p => p.DocumentName.Contains(productName) ||
p.DocumentTitle.Contains(productName));
}
else // Can only perform a "contains"
where = where.Or(p => p.DocumentName.Contains(productName) ||
p.DocumentTitle.Contains(productName));
});
// FILTER IDS - WHERE Clause
var filters = GetAllFilters().Where(x => filterIds.Contains(x.Id)).ToList();
filters.ForEach(filter =>
{
if (!filter.IsSection)
where = where.And(x => x.FilterName == filter.Name);
});
var dataSource = DocumentCollectionService.ListQuickFind(where);
var collection = new List<QuickFindResult>();
// Other UNRELATED stuff happens here...
return collection;
}
public static List<vw_QuickFindResult> ListQuickFind(Expression<Func<vw_QuickFindResult, bool>> where)
{
var connectionString = GetConnectionString(ES_DOCUMENTS_CONNECTION_NAME);
List<vw_QuickFindResult> results = null;
using (HostingEnvironment.Impersonate())
{
using (var dataContext = new ES_DocumentsDataContext(connectionString))
{
var query = dataContext.vw_QuickFindResults.Where(where).OrderBy(x => x.DocumentName).OrderBy(x => x.DocumentTitle);
results = query.ToList();
}
}
return results;
}

Joining multiple where clauses in LINQ as OR instead of AND

Is there anyway to join LINQ where clauses as OR ?
var ints = new [] { 1, 3, 5, 7 };
var query = from i in ints select i;
query = query.Where (q => q == 3);
query = query..Where (q => q == 7);
What I want is the ability to dynamically add where clauses but make them use OR instead of AND
If you want to stay with your strong-typing Linq queries you should look into LinqKit and predicate building. I have used this for something similar and found it to work well with And / Or stacking of filters.
Check out the C#4.0/3.0 in a Nutshell excerpt for more in depth info. Here is a snip from my code:
//Setup the initial predicate obj then stack on others:
basePredicate = basePredicate.And(p => false);
var predicate1 = PredicateBuilder.True<Person>();
foreach (SearchParms parm in parms)
{
switch (parm.field)
{
case "firstname":
predicate1 = predicate1.And(p => p.FirstName.Trim().ToLower().Contains(sValue));
break;
//etc...
}
}
//Run a switch based on your and/or parm value to determine stacking:
if (Parm.isAnd) {
basePredicate = basePredicate.And(predicate1);
} else {
basePredicate = basePredicate.Or(predicate1);
}
How about something like this?
var query = from i in ints where CheckConditions(i) select i;
public bool CheckConditions(int i)
{
var conditions = WhereConditions; //an IEnumerable<Func<int, bool>> of dynamically added conditions
foreach (var condition in conditions)
{
if (condition(i)) return true;
}
return false;
}
You can probably expand this to be a bit cleverer but that's sort of how I'd do it.
EDIT: Sorry the first example was an AND, have changed it now to be an OR. So the first time it encounters a passing condition it returns true.
Using ExpressionVisitor to help to build the expression base on two expressions with OR/AND relationship. This answer is from Jeffery Zhao's blog.
internal class ParameterReplacer : ExpressionVisitor
{
public ParameterReplacer(ParameterExpression paramExpr)
{
this.ParameterExpression = paramExpr;
}
public ParameterExpression ParameterExpression { get; private set; }
public Expression Replace(Expression expr)
{
return this.Visit(expr);
}
protected override Expression VisitParameter(ParameterExpression p)
{
return this.ParameterExpression;
}
}
public static Expression<Func<T, bool>> And<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
var left = parameterReplacer.Replace(one.Body);
var right = parameterReplacer.Replace(another.Body);
var body = Expression.And(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
public static Expression<Func<T, bool>> Or<T>(this Expression<Func<T, bool>> one, Expression<Func<T, bool>> another)
{
var candidateExpr = Expression.Parameter(typeof(T), "candidate");
var parameterReplacer = new ParameterReplacer(candidateExpr);
var left = parameterReplacer.Replace(one.Body);
var right = parameterReplacer.Replace(another.Body);
var body = Expression.Or(left, right);
return Expression.Lambda<Func<T, bool>>(body, candidateExpr);
}
You can using Union method:
var ints = new [] { 1, 3, 5, 7 };
var query = ints.Where(q => q == 3);
query = query.Union(ints.Where(q => q == 7));
Are you talking about specifying more than one condition in the lambda?
query = query.Where(q => q == 3 ||
q == 7);
try this
var ints = new [] { 1, 3, 5, 7 };
var query = ints.select(X=>X).where(X=>X==3||X==7);
I am trying to do something similar. Here's what I came up with:
//various test cases
bool useTestCase1 = true;
bool useTestCase2 = true;
bool useTestCase3 = false;
query = query.Where(q =>
(q == 3 && useTestCase1 ) ||
(q == 7 && useTestCase2 ) ||
(q == 10 && useTestCase3 )
);

Categories

Resources