Search code examples
linqasp.net-corecontainsany

using linq to find if a text field contains any string in a list


im running this in asp.net core v3.1

my question is similar to this question:
How to use Linq to check if a list of strings contains any string in a list

with the specific question relating to the first answer such that

filterTags = ["abc", "cd", "efg"]
var results = db.People
                .Where(p => filterTags.Any(tag => p.Tags.Contains(tag)));

so basically saying
give me results from the db of all People
who's Tags field contains any of the filterTags
where Tags = a big text field populated by a bunch of space-delimited tags

This seems straightforward (esp since this has been written before)
but i get an error back

System.InvalidOperationException: The LINQ expression 'DbSet .Where(p => __filterTags_0 .Any(tag => p.Tags.Contains(tag)))' 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()

does anyone know what this means or what im doing wrong?


Solution

  • This is not possible with pure EF LINQ. You have to create helper which transforms your search list in Expression Tree.

    public static class QueryExtensions
    {
        private static MethodInfo _containsMethodInfo = typeof(string).GetMethod("Contains")!;
    
        public static IQueryable<T> FilterUsingContains<T>(this IQueryable<T> query, Expression<Func<T, string>> prop, IList<string> items)
        {
            if (items.Count == 0)
                return query.Where(e => 1 == 2);
    
            var param = prop.Parameters[0];
    
            var predicate = items.Select(i =>
                    (Expression)Expression.Call(prop.Body, _containsMethodInfo, Expression.Constant(i, typeof(string))))
                .Aggregate(Expression.OrElse);
    
            var lambda = Expression.Lambda<Func<T, bool>>(predicate, param);
    
            return query.Where(lambda);
        }
    }
    

    Then you can use this extension in your queries

    filterTags = ["abc", "cd", "efg"]
    var results = db.People
        .Where(p => p.Tags.AsQueryable().FilterUsingContains(t => t, filterTags).Any());