使用LINQ过滤集合

时间:2009-08-24 21:03:04

标签: c# .net linq

假设我们有一组Person对象

class Person 
{
     public string PersonName {get;set;}
     public string PersonAddress {get;set;}    
}

代码定义集合中的某处

List<Person> pesonsList = new List<Person>();

我们需要一个过滤器,需要过滤集合并将结果返回给最终用户。假设我们有一个Filter类型对象的集合

class Filter 
{
    public string FieldName {get;set;}
    public string FilterString {get;set;}
}

在我们的代码中的某处

List<Filter> userFilters = new List<Filter>(); 

因此我们需要通过userFilters集合中定义的过滤器来过滤personsList集合的内容。其中 Filter.FieldName ==“PersonName”|| Filter.FieldName ==“PersonAddress”。我怎样才能以酷炫的方式使用LINQ呢?像开关/箱子,或 可能是,我认为,personList上的扩展方法是从FiledName确定要查看的Person的属性,是已知的。别的什么?有点棘手:) 谢谢。

4 个答案:

答案 0 :(得分:9)

您可以使用Expression类构建lambda表达式以创建正确的谓词。

public static Expression<Func<TInput, bool>> CreateFilterExpression<TInput>(
                                                   IEnumerable<Filter> filters)
{
    ParameterExpression param = Expression.Parameter(typeof(TInput), "");
    Expression lambdaBody = null;
    if (filters != null)
    {
        foreach (Filter filter in filters)
        {
            Expression compareExpression = Expression.Equal(
                    Expression.Property(param, filter.FieldName),
                    Expression.Constant(filter.FilterString));
            if (lambdaBody == null)
                lambdaBody = compareExpression;
            else
                lambdaBody = Expression.Or(lambdaBody, compareExpression);
        }
    }
    if (lambdaBody == null)
        return Expression.Lambda<Func<TInput, bool>>(Expression.Constant(false));
    else
        return Expression.Lambda<Func<TInput, bool>>(lambdaBody, param);
}

使用这个帮助器方法,您可以在任何IQueryable<T>类上创建扩展方法,因此这适用于每个LINQ后端:

public static IQueryable<T> Where<T>(this IQueryable<T> source, 
                                          IEnumerable<Filter> filters)
{
    return Queryable.Where(source, CreateFilterExpression<T>(filters));
}

...你可以这样打电话:

var query = context.Persons.Where(userFilters);

如果您还想支持IEnumerable<T>个收藏集,则需要使用此额外的扩展程序方法:

public static IEnumerable<T> Where<T>(this IEnumerable<T> source, 
                                           IEnumerable<Filter> filters)
{
    return Enumerable.Where(source, CreateFilterExpression<T>(filters).Compile());
}

请注意,这仅适用于字符串属性。如果您要对字段进行过滤,则需要将Expression.Property更改为Expression.Field(或MakeMemberAccess),如果您需要支持除字符串属性之外的其他类型,则您需要向Expression.Constant方法的CreateFilterExpression部分提供更多类型信息。

答案 1 :(得分:3)

你可以通过反思来做到这一点:

IQueryable<Person> filteredPersons = personsList.AsQueryable();
Type personType = typeof(Person);
foreach(Filter filter in userFilters) {
    filteredPersons = filteredPersons.Where(p => (string)personType.InvokeMember(filter.FieldName, BindingFlags.GetProperty, null, p, null) == filter.FilterString);
}

(未编译,但这应该是正确的轨道)

答案 2 :(得分:2)

你不能这样做吗

personList.Where(x => x.PersonName == "YourNameHere").ToList() ?

答案 3 :(得分:0)

我会在Filter类中添加一个方法来检查过滤器是否满足:

class Filter 
{
    public string FieldName {get;set;}
    public string FilterString {get;set;}

    public bool IsSatisfied(object o)
    { return o.GetType().GetProperty(FieldName).GetValue(o, null) as string == FilterString;
}

然后您可以像这样使用它:

var filtered_list = personsList.Where(p => userFilters.Any(f => f.IsSatisfied(p)));
相关问题