将Linq where condition添加到通用IQueryable <t>

时间:2016-03-30 17:51:33

标签: .net linq generics

基本上我想做的是:

var validValues = new List<int>() { 1, 2, 34 };

query = query.Where(item => validValues.Contains(item.Value));

除了我不知道它是什么类型之外,我想把它作为一种扩展方法:

public static IQueryable<T> Where<T>(this IQueryable<T> source, string propertyName, IList<int> accpetedValues)
{
   if (accpetedValues == null || !accpetedValues.Any() || string.IsNullOrEmpty(propertyName))
      return source;

   // HERE

   return source;
}

我发现这篇文章IQueryable Extension: create lambda expression for querying a column for a keyword 但这不是我想要的,我无法弄清楚如何使其适应我的情况...

3 个答案:

答案 0 :(得分:1)

它与链接的帖子类似,但这次您需要使用Expression<Func<T, bool>>动态构建Expression.Call,以便像这样调用Enumerable.Contains<int>

public static IQueryable<T> Where<T>(this IQueryable<T> source, string propertyName, IList<int> accpetedValues)
{
    if (accpetedValues == null || !accpetedValues.Any() || string.IsNullOrEmpty(propertyName))
        return source;

    var item = Expression.Parameter(typeof(T), "item");
    var selector = Expression.PropertyOrField(item, propertyName);
    var predicate = Expression.Lambda<Func<T, bool>>(
        Expression.Call(typeof(Enumerable), "Contains", new[] { typeof(int) },
            Expression.Constant(accpetedValues), selector),
        item);
    return source.Where(predicate);
}

答案 1 :(得分:0)

如果你使用任意集合(不仅仅是IList) - 你可以这样做:

static class Extensions {
    public static IQueryable<TEntity> WhereContains<TEntity, TProperty>(this IQueryable<TEntity> source, IList<TProperty> accpetedValues, Expression<Func<TEntity, TProperty>> property) {
        if (accpetedValues == null || !accpetedValues.Any())
            return source;

        var member = property.Body as MemberExpression;
        if (member == null)
            return source; // better throw

        string propertyName = member.Member.Name;
        var containsMethod = typeof (ICollection<TProperty>).GetMethod("Contains"); // get Contains method of IList
        var parameter = Expression.Parameter(typeof (TEntity), "p");
        var memberAccess = Expression.Property(parameter, propertyName); // p.ValidValue (for example)
        var list = Expression.Constant(accpetedValues); // your list
        var body = Expression.Call(list, containsMethod, memberAccess); // list.Contains(p.ValidValue)
        return source.Where(Expression.Lambda<Func<TEntity, bool>>(body, parameter));
    }
}

注意我也使用属性访问表达式而不是将属性名称作为字符串传递 - 这更加类型安全。使用方式如下:

var validValues = new List<int>() { 1, 2, 34 };
query = query.WhereContains(validValues, item => item.ValidValue);

答案 2 :(得分:0)

这是带有通用有效值集合的动态表达式的更好版本:

    public static IQueryable<Tsource> Where<Tsource, Tproperty>(this IQueryable<Tsource> source, Expression<Func<Tsource, Tproperty>> property, IList<int> accpetedValues)
    {
        var propertyName = ((MemberExpression)property.Body).Member.Name;

        if (accpetedValues == null || !accpetedValues.Any() || string.IsNullOrEmpty(propertyName))
            return source;

        var item = Expression.Parameter(typeof(Tsource), "item");
        var selector = Expression.PropertyOrField(item, propertyName);
        var predicate = Expression.Lambda<Func<Tsource, bool>>(
            Expression.Call(typeof(Enumerable), "Contains", new[] { typeof(Tproperty) },
                Expression.Constant(accpetedValues), selector),
            item);
        return source.Where(predicate);
    }

<强>用法:

query = query.Where(item => item.Value, validValues));