0

I have the following dynamic linq

var results=(from a in alist where a.id==id select a)
if(...something)
{
results=(from a in results where a.amount>input1 && a.typeId==1 select a)
}
if(...something else)
{
results=(from a in results where a.amount>input2 && a.typeId==2 select a)
}
if(...something else again)
{
results=(from a in results where a.amount>input3 && a.typeId==3 select a)
}

However this produces an AND statement which means all the statements need to be true for anything to be returned.

I need the last 3 statements to be ORed together.

eg I want

Where (a.id==id) && ((a.amount>input1 && a.typeId==1) || (a.amount>input2 && a.typeId==2) || (a.amount>input3 && a.typeId==3))

How do I do this?

coolblue2000
  • 3,796
  • 10
  • 41
  • 62
  • 1
    Can't you change the AND statements `&&` for OR statements `||`? Is that what you're looking? – ikerbera Nov 07 '18 at 13:17
  • I think you made an error, for it to become an `AND` you need to write `results=(from a in results ...)` – Peter B Nov 07 '18 at 13:19
  • @peter b. You are correct. I have made the change. Trigger happy cut and paste was to blame :-) – coolblue2000 Nov 07 '18 at 13:38
  • @Ikerbera. It is not those that I want to OR. It is each if the groups. For example I want results where (a.amount>input1 && a.typeId==1) || (a.amount>input2 && a.typeId==2) – coolblue2000 Nov 07 '18 at 14:36

2 Answers2

4

Check the PredicateBuilder class. This is a famous implementation of extensions methods for Linq to easily perform dynamic logic operations with OR and AND.

Given your list is of a TypeA for sample, you coul try this:

Expression<Func<TypeA, bool>> filter = a => a.id == id;

if(...something)
{
   filter = filter.Or(a => a...);
}

if(...something)
{
   filter = filter.Or(a => a...);
}

if(...something)
{
   filter = filter.Or(a => a...);
}


var results = alist.Where(filter).ToList();
Felipe Oriani
  • 37,948
  • 19
  • 131
  • 194
0

Use .Concat()

I am not absolutely sure if I understood you question correctly, but this code will create a resultset that is appended to if your if conditions are true, rather than replacing the original resultset.

var results=(from a in alist where a.id==id select a)
if(...something)
{
    results = results.Concat((from a in alist where a.amount>input1 && a.typeId==1 select a))
}
if(...something else)
{
    results = results.Concat((from a in alist where a.amount>input2 && a.typeId==2 select a))
}
//....

Edited as per Peter B's comment.

If multiple lists may contain the same element and you only wish to have every element at most once, use .Union instead of .Concat. This has some performance penalty of course (having to compare the elements).

After your edit

Your edit clarified things a bit. You have two options:

  1. Move your a.id == id check into the inner queries:

    var results=Enumerable.Empty<typeofa>()
    if(...something)
    {
        results = results.Concat((from a in alist where a.id == id && a.amount>input1 && a.typeId==1 select a))
    }
    if(...something else)
    {
        results = results.Concat((from a in alist where a.id == id && a.amount>input2 && a.typeId==2 select a))
    }
    //....
    
  2. First filter the set using the id, materialize that, then further narrow that down using the method I showed above.

    var results=Enumerable.Empty<typeofa>();
    var fileterdList = (from a in alist where a.id==id select a).ToList();
    if(...something)
    {
        results = results.Concat((from a in fileterdList where a.amount>input1 && a.typeId==1 select a))
    }
    if(...something else)
    {
        results = results.Concat((from a in fileterdList where a.amount>input2 && a.typeId==2 select a))
    }
    //....
    

    Whichever works better depends on your situation. General advice is that prefiltering is more efficient if it narrows down the list considerably and/or the original source is relatively expensive to query (sql for example), but as always, you should profile your concrete example yourself.

Marcell Toth
  • 3,433
  • 1
  • 21
  • 36
  • 1
    This may produce duplicate entries. See [Union Vs Concat in Linq](https://stackoverflow.com/q/13417556/1220550) – Peter B Nov 07 '18 at 13:44
  • Sorry, there was a mistake in my initial question. I am already appending the results but this is on an AND basis rather than an OR, so my result set returns no results. – coolblue2000 Nov 07 '18 at 14:33
  • This is how my code in this answer works. Assuming both if conditions are true you will get results where `a.id == id` OR `a.amount>input1` && `a.typeId==1 OR a.amount>input2 && a.typeId==2`. – Marcell Toth Nov 07 '18 at 15:33
  • 1
    Using a predicate builder is much better. This code executes one query for each OR condition and it contains a lot of repetitive code. – Gert Arnold Nov 07 '18 at 15:54