I've a question about the PredicateBuilder and I really hope you can give me some advice on how to solve this. I'll try to explain this.
I have the case where people can search for products based on keywords. Each keyword belongs to a keywordgroup, so some real data would be:
KeywordGroup / Keyword
Type - Chain/
Type - Bracelet/
Color - Purple/
Color - Green
Now I want to have the following results:
Between each different KeywordGroup there should be an OR. Between each different Keyword inside a KeywordGroup there should be an AND.
So e.g., a user want's to search for only Bracelets with the colors Purlple or Green.
Is this possible with this PredicateBuilder?
This is what I have so far:
================================
/// <summary>
/// Search for products
/// </summary>
/// <param name="itemsPerPage"></param>
/// <returns></returns>
public List<Product> SearchProducts(int from, int max, string sorting, List<Keyword> filter, out int totalitems) {
try {
var predicate = PredicateBuilder.True<Product>();
KeywordGroup previousKeywordGroup = null;
foreach (Keyword k in filter.OrderBy(g=>g.KeywordGroup.SortOrder)) {
if (previousKeywordGroup != k.KeywordGroup) {
previousKeywordGroup = k.KeywordGroup;
predicate = predicate.And(p => p.Keywords.Contains(k));
}
else
predicate = predicate.Or(p => p.Keywords.Contains(k));
}
var products = context.Products.AsExpandable().Where(predicate);
开发者_运维知识库
//var products = from p in context.Products
// from k in p.Keywords
// where filter.Contains(k)
// select p;
totalitems = products.Distinct().Count();
if (sorting == "asc")
return products.Where(x => x.Visible == true).Distinct().Skip(from).Take(max).OrderBy(o => o.SellingPrice).ToList();
else
return products.Where(x => x.Visible == true).Distinct().Skip(from).Take(max).OrderByDescending(o => o.SellingPrice).ToList();
}
catch (Exception ex) {
throw ex;
}
}
================================
It doesn't work, though.
Can you help me out?
Thanks! Daniel
You need to use a temporary variable in the loop for each keyword. From the Predicate Builder page:
The temporary variable in the loop is required to avoid the outer variable trap, where the same variable is captured for each iteration of the foreach loop.
Try this instead:
foreach (Keyword k in filter.OrderBy(g=>g.KeywordGroup.SortOrder)) {
Keyword temp = k;
if (previousKeywordGroup != k.KeywordGroup) {
previousKeywordGroup = k.KeywordGroup;
predicate = predicate.And(p => p.Keywords.Contains(temp));
}
else
predicate = predicate.Or(p => p.Keywords.Contains(temp));
}
Notice the use of temp
in each line where predicate And
and Or
are used.
This is just making a big list of And
ands Or
statements, you need to group them together.
Something like this..
var grouped = filter.GroupBy(item => item.KeyWordGroup, item => item.KeyWords);
foreach (var item in grouped)
{
var innerPredicate = PredicateBuilder.True<Product>();
foreach (var inner in item)
{
innerPredicate = innerPredicate.Or(p => item.Contains(k));
}
predicate = predicate.And(innerPredicate); //not sure this is correct as dont have IDE..
}
精彩评论