c# Linq的where子句根据属性名

本文关键字:属性 子句 Linq where | 更新日期: 2023-09-27 18:11:01

假设我有以下类:

public class Person { 
    public string FirstName { get; set; }
    public string SurName { get; set; }
    public int Age { get; set; }
    public string Gender { get; set; }
}

此外,我有以下方法,我正在通过存储库接触个人数据。

public IEnumerable<Person> getPeople(string searchField, string searchTerm) { 
    //_repo.GetAll() returns IEnumerable<Person>
    var model = _repo.GetAll(); 
    //Need the logic here for filtering
    return model;
}

正如你所看到的,我得到了方法的两个参数:searchFieldsearchTerm

searchField为要过滤的字段名。searchTerm是将用于与检索值进行比较的值(对不起,如果我在这里不清楚,但这是我能想到的最多的值)

我通常会这样做:

public IEnumerable<Person> getPeople(string searchField, string searchTerm) { 
    //_repo.GetAll() returns IEnumerable<Person>
    var model = _repo.GetAll(); 
    switch(searchField) { 
        case "FirstName":
            model = model.Where(x => x.FirstName == searchTerm);
            break;
        case "SurName":
            model = model.Where(x => x.SurName == searchTerm);
            break;
        //Keeps going
    }
    return model;
}

这将工作得很好。但是如果我对我的类做了一个改变,如果我在这个类中添加新的属性,这个代码将会有一个改变,或者缺少一些功能。

我要找的是下面的东西:

注意:

下面的代码完全属于我的想象,没有这样的一个东西存在。

model = model.Where(x => x.GetPropertyByName(searchField) == searchTerm);

如果这是不可能的,我是不是飞得太高了?如果已经有了这样的方法,我是不是完全是个白痴?

c# Linq的where子句根据属性名

看起来你需要动态Linq查询:

http://weblogs.asp.net/scottgu/archive/2008/01/07/dynamic-linq-part-1-using-the-linq-dynamic-query-library.aspx

我使用这个扩展方法来实现您想要的。

public static IQueryable<TEntity> Where<TEntity>(this IQueryable<TEntity> source, string propertyName, string value) 
{
    Expression<Func<TEntity, bool>> whereExpression = x => x.GetType().InvokeMember(propertyName, BindingFlags.GetProperty, null, x, null).ObjectToString().IndexOf(value, StringComparison.InvariantCultureIgnoreCase) >= 0;
    return source.Where(whereExpression);       
}

注意:ObjectToString只是另一个返回string的扩展方法。如果传入的对象是NULL,则为空

对于linq2Object,你可以像下面这样使用反射(它不是很快):

model.Where(x => x.GetType().GetProperty(propName).GetValue(x, null) == propVal);

但是对于linq2Entity,我认为这不起作用,它适用于linq2objects

我认为下面的实现看起来非常像您最初想要的,尽管将其更改为泛型方法可能更有意义。

public IEnumerable<Person> getPeople(string searchField, string searchTerm) {
        PropertyInfo getter=typeof(Person).GetProperty(searchField);
        if(getter==null) {
            throw new ArgumentOutOfRangeException("searchField");
        }
        return _repo.GetAll().Where(x => getter.GetValue(x, null).ToString()==searchTerm);
}

这应该是类型安全的:

public IEnumerable<T> Where<T,U>(Func<T,U> propertySelector, U value)
{
  return  model.Where(x => propertySelector(x) == value);
}

用法:

Where((MyClass x) => x.PropertyName, propertyValue);

或:

public IEnumerable<T> Where<T>(Func<T,bool> entitySelector)
{
  return  model.Where(entitySelector);
}

用法:

Where<MyClass>(x => x.PropertyName == propertyValue && x.OtherProperty == otherValue);

使用反射

model = model.Where(x =>
((string)x.GetType().GetProperty("searchField").GetValue(0, null)) == searchTerm);

在使用实体框架时,与其乱用反射、自定义表达式树等,不如考虑使用Builder方法扩展到接受字符串而不是lambda的标准LINQ操作符。对于动态查询需求,这些更容易构建:

 string filter = String.Format("it.{0} = @value", fieldName);
 var model = context.People.Where(filter, new ObjectParameter("value", searchValue));

当然,这意味着您需要修改存储库以返回IObjectSet而不是IEnumerable。它的性能也会更好。通过返回IEnumerable,你将数据库中的每一行都过滤到存储库中,然后通过LINQ对对象进行过滤,而不是在数据库中应用过滤器。

有关EF中的构建器方法的更多信息,请参阅http://archive.msdn.microsoft.com/EFQuerySamples/Release/ProjectReleases.aspx?ReleaseId=4422.

中的BuilderMethodSamples.cs。