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;
}
正如你所看到的,我得到了方法的两个参数:searchField
和searchTerm
。
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);
如果这是不可能的,我是不是飞得太高了?如果已经有了这样的方法,我是不是完全是个白痴?
看起来你需要动态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.