假设我们有一组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的属性,是已知的。别的什么?有点棘手:) 谢谢。
答案 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)));