Generic Linq to Entities filter method that accepts filter criteria and properties to be filtered

前端 未结 4 710
死守一世寂寞
死守一世寂寞 2021-01-01 07:35

I\'ve looked into many generic linq filtering questions and their answers here in SO but none of them satisfy my needs so I thought I should create a question.

I\'ve

4条回答
  •  旧巷少年郎
    2021-01-01 08:12

    You can do it with expression trees but it's not as simple as you might think.

    public static IQueryable Match(this IQueryable data, string searchTerm,
                                             params Expression>[] filterProperties)
    {
        var parameter = Expression.Parameter(typeof (T), "source");
    
        Expression body = null;
    
        foreach (var prop in filterProperties)
        {
            // need to replace all the expressions with the one parameter (gist taken from Colin Meek blog see link on top of class)
    
            //prop.body should be the member expression
            var propValue =
                prop.Body.ReplaceParameters(new Dictionary()
                    {
                        {prop.Parameters[0], parameter}
                    });
    
    
            // is null check
            var isNull = Expression.NotEqual(propValue, Expression.Constant(null, typeof(string)));
    
            // create a tuple so EF will parameterize the sql call
            var searchTuple = Tuple.Create(searchTerm);
            var matchTerm = Expression.Property(Expression.Constant(searchTuple), "Item1");
            // call ToUpper
            var toUpper = Expression.Call(propValue, "ToUpper", null);
            // Call contains on the ToUpper
            var contains = Expression.Call(toUpper, "Contains", null, matchTerm);
            // And not null and contains
            var and = Expression.AndAlso(isNull, contains);
            // or in any additional properties
            body = body == null ? and : Expression.OrElse(body, and);
        }
    
        if (body != null)
        {
            var where = Expression.Call(typeof (Queryable), "Where", new[] {typeof (T)}, data.Expression,
                                        Expression.Lambda>(body, parameter));
            return data.Provider.CreateQuery(where);
        }
        return data;
    }
    
     public static Expression ReplaceParameters(this Expression exp, IDictionary map)
    {
        return new ParameterRebinder(map).Visit(exp);
    }
    

    Now you need to have a expressionvisitor to make all the expressions use one parameter

    //http://blogs.msdn.com/b/meek/archive/2008/05/02/linq-to-entities-combining-predicates.aspx
    public class ParameterRebinder : ExpressionVisitor
    {
        private readonly IDictionary _map;
    
        public ParameterRebinder(IDictionary map)
        {
            _map = map;
        }
    
        protected override Expression VisitParameter(ParameterExpression node)
        {
            if (_map.ContainsKey(node))
            {
                return _map[node];
            }
            return base.VisitParameter(node);
        }
    }
    

    Would use it like

    var matches = retailers.Match("7", r => r.Address.Street, x => x.Address.Complement).ToList();
    

    Warning - I checked this with linq to objects using the AsQueryable but didn't run it against EF.

提交回复
热议问题