1

I have a case where a user is allowed to supply a set of keywords (more preceisely substrings) when searching for a user name.

I have something like

// this set of strings should find names such as john, mary or smith
List<string> searchStrings=new List<string>(){"jo","ma","th"}; 

// LINQ query looks like this

var filteredPatients=allPatients.Where(p =>
                            (searchStrings.Any(s=>p.Name.ToLower().Contains(s)));

However this fails and gives the exception:

System.AggregateException : One or more errors occurred. (The LINQ expression could not be translated. Either rewrite the query in a form that can be translated, or switch to client evaluation explicitly by inserting a call to either AsEnumerable(), AsAsyncEnumerable(), ToList(), or ToListAsync()

This is running against entityFramework, .NET Core 3.1 against a Postgres Db

Is there another way to write this query to produce the desired result?

NOTE: it would be possible to call ToList() or ToArray() on allPaitents but I would like to avoid this as the whole (very large) table will be read into memory.

shelbypereira
  • 2,097
  • 3
  • 27
  • 50
  • looks like youore just missing a closing parenthesis ")".. but that should not get you that error.. it shouldnt even compile.. what is the full code? – audzzy Oct 06 '20 at 20:51
  • allPatients is probutly an IQueryable or something like that with delayed execution. Try `allPatients.ToArray().Where`... – Thomas Oct 06 '20 at 21:02
  • @Thomas yes that is possible but that implies that I read the whole table into memory which I would like to avoid. I will update the question. – shelbypereira Oct 06 '20 at 21:03

1 Answers1

3

For starters you'll want a PredicateBuilder that will allow you to combine expressions together.

Using that, you can just create a predicate searching for each search string individually and then OR together those predicates.

List<string> searchStrings = new List<string>() { "jo", "ma", "th" };

var predicate = searchStrings.Select<string, Expression<Func<Patient, bool>>>(search => patient => patient.Name.ToLower().Contains(search))
    .DefaultIfEmpty(patient => false) //or whatever else you want to do if there are no search strings
    .Aggregate(PredicateBuilder.Or);
            
var filteredPatients = allPatients.Where(predicate);
Servy
  • 202,030
  • 26
  • 332
  • 449