If you're adding "and" conditions to a Linq query, it's easy to do it like so:
var q = MyTable;
if (condition1)
q = q.Where(t => t.Field1 == value1);
if (condition2)
q = q.Where(t => t.Field2 > t.Field3);
// etc.
Is there any clever way of doing the same thing, when you want to add "or" conditions?
You can use a PredicateBuilder and use it to build an Or based expression:
var predicate = PredicateBuilder.False<Product>();
predicate = predicate.Or (t => t.Field1 == value1);
predicate = predicate.Or (t => t.Field2 > t.Field3);
q = q.Where (predicate);
You can read more about it here:
http://www.albahari.com/nutshell/predicatebuilder.aspx
Replace the Product in PredicateBuilder.False<Product>() with your queried object.
Note that you start from a False predicate as you want to use Or. If You'd want an And predicate, Yuo should start from a True
Use the following:
var q = MyTable;
q = q.Where(
t => (condition1 && t.Field1 == value1) || (condition2 && t.Field2 > t.Field3));
var q = MyTable;
var conditions = new List<Func<T, bool>>();
if (condition1)
conditions.Add(t => ...);
if (condition2)
conditions.Add(t => ...);
q.Where(x => conditions.Any(y => y(x)));
There is one way to do this that involves using expression trees. This way you build the Boolean expression yourself. It's pretty straight forward the tricky part though is that you need to rebase the parameters because otherwise it is going to refer the original lambda expression. See below for an example:
static void Main(string[] args)
{
var source = new List<int> { 1, 2, 3 };
var any = new List<Expression<Func<int, bool>>>();
any.Add(x => x == 1);
any.Add(x => x == 3);
foreach (var item in source.AsQueryable().WhereDisjunction(any))
{
Console.WriteLine(item);
}
}
class RewriteSingleParameterUsage : ExpressionVisitor
{
public ParameterExpression Parameter { get; set; }
protected override Expression VisitParameter(ParameterExpression node)
{
return Parameter;
}
}
public static IQueryable<T> WhereDisjunction<T>(this IQueryable<T> source, IList<Expression<Func<T, bool>>> any)
{
switch (any.Count)
{
case 0: return source;
case 1: return source.Where(any[0]);
default:
var p = Expression.Parameter(any[0].Parameters[0].Type, any[0].Parameters[0].Name);
var rw = new RewriteSingleParameterUsage { Parameter = p };
var expr = rw.Visit(any[0].Body);
for (int i = 1; i < any.Count; i++)
{
expr = Expression.Or(expr, rw.Visit(any[i].Body));
}
return source.Where(Expression.Lambda<Func<T, bool>>(expr, p));
}
}
In the above example I'm being very harsh, I'm effectively replacing any parameter with this single new parameter that is being used to create the new expression. However, given the signature of this extension method it shouldn't really be possible to call this method with parameters such that it would cause an error. It is however going to be a problem if you involve more than one parameter.
This is the same answer I gave here
As Marc Gravell said it involves expression-tree combining.
This article shows you how to do that. It takes a bit of work to initially set it up. But its worth it.
Alternate solution is to use Predicate Builder. The article does not explain very well what is actually happening under-the-hood. But the above article explains it nicely
Related
private readonly IRepository<Order> _orderRepo; // initialized in the constructor
IRepository<Order> GetOrder(string orderstate)
{
if(orderstate == null)
{
return null;
}
IQueryable<Order> query = null;
if(orderstate == "OrderStateChanged")
{
query = (from c in _orderRepo.Table
where c.OrderStateChanged != 0
select c
);
}
else if (orderstate == "PaymentStateChanged")
{
query = (from c in _orderRepo.Table
where c.PaymentStateChanged != 0
select c
);
}
/*More else if statement*/
}
I used LINQ to extract data from the repo and I have more else if statement. I want to remove the repetitive behavior of my code.
I have a clue "Expression Tree" but I can't understand How can I use that in my code Or suggest me any other way to remove
repetitiveness.
If you really want to prevent manually mapping orderState to members (using if/else, switch, IDictionary, ...), your only option are indeed expression trees:
var orderType = typeof(Order);
var param = Expression.Parameter(orderType);
var member = orderType.GetMember(orderState).FirstOrDefault();
if (member == null)
{
/* requested member of "Order" does not exist */
}
var filter = Expression.Lambda<Func<Order, bool>>( // "param => param.member != 0"
Expression.NotEqual( // "param.member != 0"
Expression.MakeMemberAccess(param, member), // "param.member"
Expression.Constant(0)), // "0"
param); // "param =>"
query = _orderRepo.Table.Where(filter);
Alternative (simpler, but throws ArgumentException if the member doesn't exist)
var orderType = typeof(Order);
var param = Expression.Parameter(orderType);
var member = Expression.PropertyOrField(param, orderState); // may throw ArgumentException!
var filter = Expression.Lambda<Func<Order, bool>>(
Expression.NotEqual(member, Expression.Constant(0)),
param);
query = _orderRepo.Table.Where(filter);
This way you are really generic, even if the object layout of Order changes. One disadvantage is of course the risk of invalid orderState values leading to non-existing members of Order but I'm sure you had some kind of mechanism already, anyway.
Further reading
MSDN - Expression Trees
MSDN - How to: Use Expression Trees to Build Dynamic Queries
CodeProject - Expression Tree Basics
You won't get it much better, but the extension method syntax is (IMHO) a bit more concise for this sort of code
IEnumerable<Order> GetOrder(string orderstate)
{
if(orderstate == null)
{
return null;
}
IQueryable<Order> query = _orderRep.Table;
if(orderstate == "OrderStateChanged")
{
query = query.Where(c => c.OrderStateChanged != 0);
}
else if (orderstate == "PaymentStateChanged")
{
query = query.Where(c => c.PaymentStateChanged != 0);
}
/*More else if statement*/
}
If you need this filtering in multiple places, you could maintain some kind of dictionary with the expression predicates:
static IDictionary<string, Expression<Func<Order,bool>> Predicates = new Dictionary<string, Expression<Func<Order,bool>>
{
{"OrderStateChanged", o => o.OrderStateChanged != 0},
{"OrderPaymentChanged", o => o.PaymentStateChanged != 0},
};
Then your method could become something like:
IEnumerable<Order> GetOrder(string orderstate)
{
if (orderstate == null || !Predicates.ContainsKey(orderstate))
return null; // or throw exception
var predicate = Predicates[orderstate];
return _orderRep.Table.Where(predicate);
}
LINQ is composable by nature. This means that you can do this:
var query = _orderRepoTable.AsQueryable();
switch (orderState)
{
case "OrderStateChanged": query = query.Where(c => c.OrderStateChanged != 0); break;
...
}
// Now query has the filters you want.
I'd still stick with the explicit filters, rather than using expression trees to build them based on orderState directly - that's just asking for trouble. Using an enum for orderState might also be a good idea. Don't worry, this isn't really code repetition - it's just a definition of a language, basically.
This question already has answers here:
Generate EF orderby expression by string [duplicate]
(6 answers)
Closed 7 years ago.
I am using .NET 4.51, EF 6
I make a number of calls to my repository layer where I need to do some basic ordering on a single field in either ascending or descending order such as:
The result of GetAllList() is a List<T>. Now unfortunately the Id field I have to sort by is not always called Id nor is the Text field. They can be other things such as MyId, SomeTextField and so on.
So I was wondering if there was a way I could do the OrderBy() and OrderByDescending() clauses by supplying a string for the field name something like:
_Repository.GetAllList().OrderBy(r => r."SomeTextField")
In this way I could move all this code to a common method.
Any pointers greatly appreciated.
This will work:
public static class LinqExtensions
{
private static PropertyInfo GetPropertyInfo(Type objType, string name)
{
var properties = objType.GetProperties();
var matchedProperty = properties.FirstOrDefault (p => p.Name == name);
if (matchedProperty == null)
throw new ArgumentException("name");
return matchedProperty;
}
private static LambdaExpression GetOrderExpression(Type objType, PropertyInfo pi)
{
var paramExpr = Expression.Parameter(objType);
var propAccess = Expression.PropertyOrField(paramExpr, pi.Name);
var expr = Expression.Lambda(propAccess, paramExpr);
return expr;
}
public static IEnumerable<T> OrderBy<T>(this IEnumerable<T> query, string name)
{
var propInfo = GetPropertyInfo(typeof(T), name);
var expr = GetOrderExpression(typeof(T), propInfo);
var method = typeof(Enumerable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
return (IEnumerable<T>) genericMethod.Invoke(null, new object[] { query, expr.Compile() });
}
public static IQueryable<T> OrderBy<T>(this IQueryable<T> query, string name)
{
var propInfo = GetPropertyInfo(typeof(T), name);
var expr = GetOrderExpression(typeof(T), propInfo);
var method = typeof(Queryable).GetMethods().FirstOrDefault(m => m.Name == "OrderBy" && m.GetParameters().Length == 2);
var genericMethod = method.MakeGenericMethod(typeof(T), propInfo.PropertyType);
return (IQueryable<T>) genericMethod.Invoke(null, new object[] { query, expr });
}
}
Testing:
var r = new List<temp> {
new temp { a = 5 },
new temp { a = 1 },
new temp { a = 15 }
}.OrderBy("a");
Gives the correct result (1, 5, 15) - and will provide lazy execution for your use with EF
You will need to implement the overloads if needed.
Does it have to be a string? Why not just make a method that takes a Func key selector as a parameter.
public List<T> GetAllListOrdered<T,TProp>(SimpleOrderingDirectionEnum direction, Func<T,TProp> keySelector)
{
return direction == SimpleOrderingDirectionEnum.Ascending ? _Repository.GetAllList().OrderBy(keySelector).ToList() : _Repository.GetAllList().OrderByDescending(keySelector).ToList();
}
Then call it like
Func<ObjectToSortType, ObjectPropertyToSortBy> keySelector = r => r.Id;
GetAllListOrdered(SimpleOrderingDirectionEnum.Ascending, keySelector);
If the Rob's answer is not enough for you. Try Linq Dynamic. http://dynamiclinq.azurewebsites.net/
using System.Linq.Dynamic; //Import the Dynamic LINQ library
//The standard way, which requires compile-time knowledge
//of the data model
var result = myQuery
.Where(x => x.Field1 == "SomeValue")
.Select(x => new { x.Field1, x.Field2 });
//The Dynamic LINQ way, which lets you do the same thing
//without knowing the data model before hand
var result = myQuery
.Where("Field1=\"SomeValue\"")
.Select("new (Field1, Field2)");
Thanks to all. Rob, your solution was pretty close to what I ended up with.
Based on your insights I did some more searching and came across Marc Gravel's answer here Dynamic LINQ OrderBy on IEnumerable<T> (second post).
It added dynamic's as an additional bonus.
I have an expression like this:
Expression<Func<int, bool>> exp = i => i<15 && i>10;
I want to add a condition to exp after this line. How can I do this?
Simply with this:
Expression<Func<int, bool>> exp = i => i < 15 && i > 10;
var compiled = exp.Compile();
exp = i => compiled(i) && i % 2 == 0; //example additional condition
Note that you can't do it like this:
exp = i => exp.Compile()(i) && i % 2 == 0; //example additional condition
because exp will be added to the closure by reference and as a result, calling it will cause a StackOverflowException.
You have two options. The first one is the version of BartoszKP, to blackbox the first expression and use it afterwards. However, while this has a great syntax support, it also means that systems like the Entity Framework cannot really use the expression, because it is blackboxed. If this expression was used in a database query, the EF could not check this predicate on the server, but has to retrieve all the data to the client, if it works at all.
Thus, if you want to use the expression e.g. for a database query, you have to use the Expression API, i.e.
Expression<Func<int, bool>> exp = i => i<15 && i>10;
exp = Expression.Lambda<Func<int, bool>>(Expression.AndAlso(exp.Body, ...), exp.Parameters[0]);
The three dots indicate the expression that you want to insert as second part. You could use another expression created by the compiler, but you would then have to replace the parameters.
I found the answer from https://entityframework.net/ for .Net Framework 6.0
which also worked for me with .net core
class Program
{
static void Main(string[] args)
{
Expression<Func<int, bool>> exprA = a => a == 3;
Expression<Func<int, bool>> exprB = b => b == 4;
Expression<Func<int, bool>> exprC =
Expression.Lambda<Func<int, bool>>(
Expression.OrElse(
exprA.Body,
new ExpressionParameterReplacer(exprB.Parameters, exprA.Parameters).Visit(exprB.Body)),
exprA.Parameters);
Console.WriteLine(exprA.ToString());
Console.WriteLine(exprB.ToString());
Console.WriteLine(exprC.ToString());
Func<int, bool> funcA = exprA.Compile();
Func<int, bool> funcB = exprB.Compile();
Func<int, bool> funcC = exprC.Compile();
Debug.Assert(funcA(3) && !funcA(4) && !funcA(5));
Debug.Assert(!funcB(3) && funcB(4) && !funcB(5));
Debug.Assert(funcC(3) && funcC(4) && !funcC(5));
}
}
Note that: ExpressionParameterReplacer is a helper class that you should put it in your helpers or anywhere accessible, and does not exists in standard packages.
public class ExpressionParameterReplacer : ExpressionVisitor
{
public ExpressionParameterReplacer(IList<ParameterExpression> fromParameters, IList<ParameterExpression> toParameters)
{
ParameterReplacements = new Dictionary<ParameterExpression, ParameterExpression>();
for (int i = 0; i != fromParameters.Count && i != toParameters.Count; i++)
ParameterReplacements.Add(fromParameters[i], toParameters[i]);
}
private IDictionary<ParameterExpression, ParameterExpression> ParameterReplacements
{
get;
set;
}
protected override Expression VisitParameter(ParameterExpression node)
{
ParameterExpression replacement;
if (ParameterReplacements.TryGetValue(node, out replacement))
node = replacement;
return base.VisitParameter(node);
}
}
Sample in my own scenario
My normal usage of this was to add multiple conditions together in one of my services, which I do not have direct access to the Query so I cannot use multiple .Where() functions:
Expression<Func<Order, bool>> filter = w => ...;
// Extra complex filters which I do not feed to my request models
Expression<Func<Order, bool>> filter2 = null;
switch (model.PredefinedFilter)
{
case OrderPredefinedFilterEnum.SupportPending:
filter2 = w => (((w.Cart.CartFlow == CartFlowEnum.Buyer_First_Order_Request &&
w.Cart.CartStatus == CartStatusEnum.PaidByBuyer)
|| (w.Cart.CartFlow == CartFlowEnum.Seller_First_Suggestion &&
w.Cart.CartStatus ==
CartStatusEnum.WaitingForPaymentConfirmByBuyer)) &&
w.Cart.CartSupportStatus == CartSupportStatusEnum.Waiting);
break;
...
}
if(filter2 != null)
filter = Expression.Lambda<Func<Order, bool>>(Expression.AndAlso(filter.Body,
new ExpressionParameterReplacer(filter2.Parameters, filter.Parameters).Visit(filter2.Body)),
filter.Parameters[0]);
result = (await _orderRepository.GetAllAsNoTrackingAsync(
a => totalCount = a,
filter,
selector,
OrderBy,
take,
skip, cancellationToken: cancellationToken)).ToList();
Thanks:
#Georg answer helped me in this matter. thank you.
Also #BartoszKP asnwer is cool and simple, but doesn't work with EF and query, so I think it is a good solution for in memory data...
I am trying to edit a search tool using linq,
What I like a filter in where clause is (ItemNumber == X AND ( StatementStatus == SatusA Or StatementStatus == StatusB ) )
But right now, it is like:
What I like a filter in where clause is (ItemNumber == X AND StatementStatus == SatusA Or StatementStatus == StatusB )
as AND has higher operational priority over OR the result is not what I want. :)
Could you please help?
using (var ctx = new MyContext()) {
Func<Statement, bool> filter = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filter = new Func<Statement, bool>(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
if (request.StatusA)
filter = filter == null ? new Func<Statement, bool>(s => s.StatementStatus == StatementStatusType.StatusA) :
filter.And(s => s.StatementStatus == StatementStatusType.StatusA);
if (request.StatusB)
filter = filter == null ? new Func<Statement, bool>(s => s.StatementStatus == StatementStatusType.StatusB) :
filter.Or(s => s.StatementStatus == StatementStatusType.StatusB);
var results = ctx.Statements
.Include("StatementDetails")
.Include("StatementDetails.Entry")
.Where(filter)
.Take(100)
.Select(s => new StatementSearchResultDTO{ ....
}
}
That's happens not because AND have higher priority than OR. What happens in reality:
var firstFilter = ...; // itemNumber
var secondFilter = ...; // statusA
var firstAndSecondFilter = firstFilter.And(secondFilter); // itemNumber && statusA
var thirdFilter = ...; // statusB
var endFilter = firstAndSecondFilter.Or(thirdFilter) // (itemNumber && statusA) || statusB.
The problem - wrong control flow. You must to do something like that:
var filterByA = ...;
var filterByB = ...;
var filterByAorB = filterByA.Or(filterByB);
var filterByNumber = ...;
var endFiler = filterByNumber.And(filterByAorB);
And your code is bad, not just because it works wrong, but because it's hard to write code in such style. Reasons:
This code doesn't follow DRY principle. You have two same lambdas that checks for StatusA (look in your ternary operator) and two same lambdas that checks for StatusB
You have too long ternary operator with null checks. That's bad because you don't see general picture, your eyes focused on syntax problems. You may write and extension method AndNullable for funcs. Like this:
static Func<T1, TOut> AndNullable<T1, TOut>(this Func<T1, TOut> firstFunc, Func<T1, TOut> secondFunc) {
if (firstFunc != null) {
if (secondFunc != null)
return firstFunc.And(secondFunc);
else
return firstFunc;
}
else {
if (secondFunc != null)
return secondFunc;
else
return null;
}
}
And that same for Or. Now your code can be wroted like this:
Func<Statement, bool> filter = null;
if (request.StatusA)
filter = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filter = filter.OrNullable(s => s.StatementStatus == StatementStatusType.StatusB);
if (!string.IsNullOrEmpty(request.ItemNumber))
filter = filter.AndNullable(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
Reads more better.
Your filter is global filter. Writing of global filter is simpler for few filter conditions and number of lines is small, but it's more complicated to understand your filter. Rewrite it in this way:
Func<Statement, bool> filterByStatusA = null;
Func<Statement, bool> filterByStatusB = null;
if (request.StatusA)
filterByStatusA = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filterByStatusB = s => s.StatementStatus == StatementStatusType.StatusB;
Func<Statement, bool> filterByStatuses = filterByStatusA.OrNullable(filterByStatusB);
Func<Statement, bool> filterByItemNumber = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filterByItemNumber = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
Func<Statement, bool> endFilter = filterByItemNumber.And(filterByStatuses);
Okay, we have outthinked how we can write filters by combining them as Func<..> but we still have problems.
What problems we will got, if result filter is null? Answer: ArgumentNullException due to documentation. We must to think about this case.
What another problems we can got with using of simple Func<...>? Well, you must to know difference between IEnumerable<T> and IQueryable<T> interfaces. In simple words, all operations on IEnumerable causes simple iteratation over all elements (well, it's lazy, IEnumerable really slower than IQueryable). So, for example, combining of Where(filter), Take(100), ToList() on collection that have 10000 elements that are bad for this filter and 400 elements that are good will cause iterating over 10100 elements. If you wrote similar code for IQueryable the request of filtering will send on database server and this server will iterate only ~400 (or 1000, but not 10100), if you have configured indexes on database. So what happens in your code.
var results = ctx.Statements // you are getting DbSet<Statement> that implements interface IQueryable<Statement> (and IQueryable<T> implements IEnumerable<T>)
.Include("StatementDetails") // still IQueryable<Statement>
.Include("StatementDetails.Entry") // still IQueryable<Statement>
.Where(filter) // Cuz your filter is Func<..> and there are no extension methods on IQueryable that accepts Func<...> as parameter, your IQueryable<Statement> casted automatically to IEnumerable<Statement>. Full collection will be loaded in your memory and only then filtered. That's bad
.Take(100) // IEnumerable<Statement>
.Select(s => new StatementSearchResultDTO { .... // IEnumerable<Statement> -> IEnumerable<StatementSearchResultDTO>
}
Okay. Now you understand the problem. So, simple right code for you can be writed in this way:
using (var ctx = new MyContext()) {
results = ctx.Statements
.Include("StatementDetails")
.Include("StatementDetails.Entry")
.AsQueryable();
if (!string.IsNullOrEmpty(request.ItemNumber))
results = results.Where(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
if (request.StatusA) {
if (request.StatusB)
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusA ||
s.StatementStatus == StatementStatusType.StatusA);
else
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusA);
}
else {
if (request.StatusB) {
results = results.Where(s => s.StatementStatus == StatementStatusType.StatusB);
}
else {
// do nothing
}
}
results = .Take(100)
.Select(s => new StatementSearchResultDTO{ ....
};
// .. now you can you results.
}
Yeah, totally ugly, but now your database solves how to find Statements that satisfy the filter. Therefore, this request is quickly as possible. Now we must understand what magic happens in code I written upper. Let's compare two examples of code:
results = results.Where(s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber));
And this:
Func<Statement, bool> filter = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
results = results.Where(filter);
What the difference? Why first is more faster? Answer: when compiler sees first code, it examines that type of results is IQueryable<T> and IEnumerable<T> so that condition inside of brackets can have type Func<Statement, bool> (compiled function) or Expression<Func<Statement, bool>> (data, that can be compiled in function). And compiler chooses Expression (why - really dunno, just chooses). After request of first object query compiled not in C# statement, but in SQL statement and sends to server. Your SQL server can optimize request, because of indexes existing.
Well, the more better way - to write your own expressions. There are different ways to write your own expression, but there is a way to write it with not ugly syntax. The problem that you can't just invoke one expression from another - that doesn't supported by Entity Framework and can be not supported by another ORM's. So, we can use PredicateBuilder by Pete Montgomery: link. And then write two simple extensions on expressions suitable for us.
public static Expression<Func<T, bool>> OrNullable<T>(this Expression<Func<T, bool>> first, Expression<Func<T, bool>> second)
{
if (first != null && second != null)
return first.Compose(second, Expression.OrElse);
if (first != null)
return second;
if (second != null)
}
And that same for And. And now we can write our filter:
{
Expression<Func<Statement, bool>> filterByStatusA = null;
Expression<Func<Statement, bool>> filterByStatusB = null;
if (request.StatusA)
filterByStatusA = s => s.StatementStatus == StatementStatusType.StatusA;
if (request.StatusB)
filterByStatusB = s => s.StatementStatus == StatementStatusType.StatusB;
Expression<Func<Statement, bool>> filterByStatuses = filterByStatusA.OrNullable(filterByStatusB);
Expression<Func<Statement, bool>> filterByItemNumber = null;
if (!string.IsNullOrEmpty(request.ItemNumber))
filterByItemNumber = s => s.StatementDetails.Any(sd => sd.ItemNumber == request.ItemNumber);
Expression<Func<Statement, bool>> endFilter = filterByItemNumber.And(filterByStatuses);
requests = ...;
if (endFilter != null)
requests = requests.Where(endFilter);
}
You can got a problem, because class ExpressionVisitor in PredicateBuilder in .NET < 4.0 is sealed. You can get write your own ExpressionVisitor or just copy it from this article.
OK, here is the way I have solved it:
filter.And(s => (request.StatusA && s.StatementStatus == StatementStatusType.StatusA) ||
(request.StatusB && s.StatementStatus == StatementStatusType.StautsB) ||
!(request.StatusA || request.StatusB)); //None selected = All selected
Any comments?
I've been looking on google but not finding anything that does the trick for me.
as you know SQL has a "where x in (1,2,3)" clause which allows you to check against multiple values.
I'm using linq but I can't seem to find a piece of syntax that does the same as the above statement.
I have a collection of category id's (List) against which I would like to check
I found something that uses the .contains method but it doesn't even build.
You have to use the Contains method on your id list:
var query = from t in db.Table
where idList.Contains(t.Id)
select t;
The syntax is below:
IEnumerable<int> categoryIds = yourListOfIds;
var categories = _dataContext.Categories.Where(c => categoryIds.Contains(c.CategoryId));
The key thing to note is that you do the contains on your list of ids - not on the object you would apply the in to if you were writing sql.
Here's an article illustrating the approach. You should indeed use the Contains method over your collection which will be translated into IN clause.
Here is my realization of WhereIn() Method, to filter IQueryable collection by a set of selected entities:
public static IQueryable<T> WhereIn<T,TProp>(this IQueryable<T> source, Expression<Func<T,TProp>> memberExpr, IEnumerable<TProp> values) where T : class
{
Expression predicate = null;
ParameterExpression param = Expression.Parameter(typeof(T), "t");
bool IsFirst = true;
MemberExpression me = (MemberExpression) memberExpr.Body;
foreach (TProp val in values)
{
ConstantExpression ce = Expression.Constant(val);
Expression comparison = Expression.Equal(me, ce);
if (IsFirst)
{
predicate = comparison;
IsFirst = false;
}
else
{
predicate = Expression.Or(predicate, comparison);
}
}
return predicate != null
? source.Where(Expression.Lambda<Func<T, bool>>(predicate, param)).AsQueryable<T>()
: source;
}
And calling of this method looks like:
IQueryable<Product> q = context.Products.ToList();
var SelectedProducts = new List<Product>
{
new Product{Id=23},
new Product{Id=56}
};
...
// Collecting set of product id's
var selectedProductsIds = SelectedProducts.Select(p => p.Id).ToList();
// Filtering products
q = q.WhereIn(c => c.Product.Id, selectedProductsIds);