0

First time using c# and Linq. I have a string coming in through my route. I want to search a couple of different columns for the values in my string. Assuming I'm splitting each word on a space, foreach one of these items I want to dynamically add a .Where to my linq statement. I'm thinking I may need to dynamically add an .Or as well.

foreach (string q in query)
{
    results = results.Where(u => u.Name.Contains(r));
    results = results.Where(u => u.Text.Contains(r));
}

I'm used to JS where you could do something like results += results.Where(...) I'm not sure the appropriate way to structure this kind of thing using linq.

edit: here is the entire method for clarity

            using (var context = new MessageContext())
        {
            string[] words = query.Split(" ");
            var messages = (from m in context.Messages
                          join u in context.Users on m.UserId equals u.UserID
                          select new
                          {
                              m.Id,
                              m.Date,
                              m.Name,
                              m.Text,
                              m.UserId,
                              u.Image
                          });

            foreach (string word in words)
            {
                messages = messages.Where(u => u.Name.Contains(word)).Union(messages.Where(u => u.Text.Contains(word)));

            return messages.ToList();
        }
Christopher Mellor
  • 470
  • 2
  • 9
  • 23
  • `Or` would be part of `Where`, wouldn't it? Like `.Where(x => x == "a" || x == "b")` – ProgrammingLlama Jun 14 '19 at 04:16
  • you can use either [Expression Tree](https://stackoverflow.com/a/9525578/2417602) or you can use [Dynamic Linq](https://stackoverflow.com/a/9505238/2417602) – vikscool Jun 14 '19 at 04:18
  • but then how do I keep it from overwriting the data that is already contained in `results.` @ John – Christopher Mellor Jun 14 '19 at 04:25
  • @ChristopherMellor John's example shows you how. Don't say `results = this` and then `results = that`. Just get the results once and look for `u.Name.Contains(r) || u.Text.Contains(r)`. That will make 1 list with all matching elements. There is also `.addRange()` - either way will work, but I'd go for the 1-off query. – Reinstate Monica Cellio Jun 14 '19 at 05:02
  • @Christopher You're not overwriting, you're chaining. `.Where(a => a.Opt1 == true).Where(a => a.Opt2 = false)` is equivalent to `.Where(a => a.Opt1 == true && a.Opt2 == false)` – ProgrammingLlama Jun 14 '19 at 07:33
  • Also, you can use LinqKit check it here http://www.albahari.com/nutshell/linqkit.aspx – Cihan Yakar Jun 14 '19 at 09:42
  • did you look for predicate builder? [as example](https://www.c-sharpcorner.com/UploadFile/c42694/dynamic-query-in-linq-using-predicate-builder/) – Power Mouse Jun 14 '19 at 15:19
  • Sorry for my misunderstanding... are you saying I should do something like this? `var messages = (from m in context.Messages select m);` `foreach (string q in query) { messages.Where(u => u.Name.Contains(r) || u.Text.Contains(r)); }` `return messages` – Christopher Mellor Jun 14 '19 at 18:40

2 Answers2

2

Linq uses lazy evaluation (the results are not evaluated until you start to iterate over the results, or until you call a method like ToList()). As John pointed out each successive call is really just modifiying the search criteria. therefore in your example

results = results.Where(u => u.Name.Contains(r));
results = results.Where(u => u.Text.Contains(r));

is equivalent to

results = results.Where(u => u.Name.Contains(r)).Where(u => u.Text.Contains(r));

which implies a AND condition. If you want an OR condition you would need to us the Union operator.

results = results.Where(u => u.Name.Contains(r)).Union(results.Where(u => u.Text.Contains(r)));

The benefit of this lazy evaluation is that you can extract a base query and add on additional search criteria, thus simplifying your code.

I hope this helps.

James
  • 168
  • 8
  • Thank you for your explanation -- it clarified a lot. However, Linq doesn't seem to be doing the lazy evaluation you mentioned above but maybe I'm misusing it. if you look at the edit above perhaps you could provide some insight? As I debug through it, the first time through the `foreach` it seems that `messages` actually does get some of the messages filtered out. by the next time through the foreach it is searching for messages that contain the second item in the query inside messages that were already filtered to contain the first item... if that makes sense... – Christopher Mellor Jun 16 '19 at 02:10
0
foreach (string q in query)
{
   results = results.Where(u => u.Name.Contains(r) || u.Text.Contains(r));
}

Or may be you need to elaborate your question a bit more

Jojo
  • 47
  • 1
  • 8