我有以下情况:
一种方法,用于获取某个类的人员列表,这些人员的列表短于某个高度阈值。我正在使用Where扩展方法来过滤它们。由于谓词依赖于某些外部数据,因此classId
和height
变量成为lambda闭包的一部分。
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(p => p.Height <= height && p.ClassId == classId).ToList();
}
问题是有时候谓词变得复杂,所以我想把它提取到另一个方法:
public List<Person> getPeopleFromClassLowerThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(isLowerThan).ToList();
}
private bool isLowerThan(Person person)
{
}
然而,这里的闭包成为问题,因为我无法将变量传递给谓词函数。我可以使内联的委托函数(不使用lambdas)使谓词看起来像一个函数,但是此函数也必须与Where()
调用的方法相同。在大多数情况下,没关系,但有时我想要一个外部函数(在编译时未知)被称为Where
谓词。
我最接近解决方案的是:
public delegate bool ExtendedPredicate<T, TArg>(T argument, TArg[] arguments);
public static class Extended
{
public static IEnumerable<T> Where<T, TArg>(this IEnumerable<T> collection, ExtendedPredicate<T, TArg> predicate, params TArg[] arguments)
{
foreach (T item in collection)
{
if (predicate(item, arguments))
yield return item;
}
yield break;
}
}
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(isFromClassShorterThanLimit, classId, height).ToList();
}
public bool isFromClassShorterThanLimit(Person person, int[] arguments)
{
return person.Height <= arguments[0] && person.ClassId == arguments[1];
}
但我发现它有点太窄了,而且,对于每个扩展方法,都必须编写一个新的重载。除此之外,我想EntityFramework将无法将这些表达式转换为合理的SQL语句(不确定这一点,我不是EF如何从lambdas生成SQL的专家,但这是一个疯狂的猜测)。 (可能提供或不提供可变数量的参数)
问题:
我想知道的是,是否有任何聪明的方法可以通过常规委托(不强制编译时定义的函数作为谓词)来实现这一点,而不会出现如上所示的黑客攻击?
答案 0 :(得分:2)
这不适合你吗?
public List<Person> getPeopleFromClassLowerThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(p => isLowerThan(p, classId, height)).ToList();
}
private bool isLowerThan(Person person, int classId, int height)
{
}
答案 1 :(得分:0)
为什么不简单地将你的方法称为普通方法的谓词:
public List<Person> getPeopleFromClassShorterThanLimit(int classId, int height)
{
Database db = new Database();
return db.Persons.Where(x=>isFromClassShorterThanLimit(classId, height)).ToList();
}