Search code examples
c#linqlinq-to-sql

Overload for LINQ List.Contains() to accept column name


I would like to modify the following procedure

        public async Task<IActionResult> GetMany(List<int> values)
        {
             List<TEntity> entities = await _crudApiDbContext.Set<TEntity>()
                    .Where(entity => values.Contains(entity.Id)).ToListAsync();
             return new OkObjectResult(entities);
        }

to accept the column name which will be searched in the Contains method, something like:

        public async Task<IActionResult> GetMany(List<int> values, string colName)
        {
             List<TEntity> entities = await _crudApiDbContext.Set<TEntity>()
                    .Where(entity => values.Contains<TEntity>(colName)).ToListAsync();
             return new OkObjectResult(entities);
        }

I've tried overloading Contains and using expression trees but got lost...

Any ideas?


Solution

  • Introduce the following extension method, which should filter any entity type by collection:

    public static class QueryableExtensions
    {
        public static IQueryable<TEntity> FilterByList<TEntity, TKey>(this IQueryable<TEntity> query, IEnumerable<TKey> values, string colName) 
            where TEntity : class
        {
            var param = Expression.Parameter(typeof(TEntity), "e");
            var propAccess = Expression.PropertyOrField(param, colName);
            var listExpr = Expression.Constant(values);
    
            var predicate = Expression.Call(typeof(Enumerable), "Contains", new[] {typeof(TKey)}, listExpr,
                propAccess);
    
            var predicateLambda = Expression.Lambda<Func<TEntity, bool>>(predicate, param);
    
            return query.Where(predicateLambda);
        }
    }
    

    And rewrite your GetMany to this variant:

    public async Task<IActionResult> GetMany<TEntity>(List<int> values, string colName)
    {
        var entities = await _crudApiDbContext.Set<TEntity>()
            .FilterByList(values, colName)
            .ToListAsync();
        return new OkObjectResult(entities);
    }